func (p *Plugin) DeleteMemory(in *Msg, k string) {
	var err error
	if in.User.ID > 0 {
		q := `DELETE FROM states
		      WHERE userid=$1 AND pluginname=$2 AND key=$3`
		_, err = p.DB.Exec(q, in.User.ID, p.Config.Name, k)
	} else {
		q := `DELETE FROM states
		      WHERE flexid=$1 AND flexidtype=$2 AND pluginname=$3
		      AND key=$4`
		_, err = p.DB.Exec(q, in.User.FlexID, in.User.FlexIDType,
			p.Config.Name, k)
	}
	if err != nil {
		p.Log.Infof("could not delete memory for key %s. %s", k,
			err.Error())
	}
}
DeleteMemory deletes a memory for a given key. It is not an error to delete
a key that does not exist.
package dt
import (
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"time"
	"github.com/itsabot/abot/core/log"
	"github.com/jmoiron/sqlx"
)
type Plugin struct {
	Config      PluginConfig
	SM          *StateMachine
	Keywords    *Keywords
	Trigger     *StructuredInput
	States      []State
	DB          *sqlx.DB
	Log         *log.Logger
	Events      *PluginEvents
	SetBranches func(in *Msg) [][]State
}
type PluginConfig struct {
	Name string
	Icon string
	Maintainer string
	ID uint64
	Usage []string
	Settings map[string]*PluginSetting
	Tests []map[string][]string
}
type PluginSetting struct {
	Required bool
	Default  string
}
type PluginEvents struct {
	PostReceive    func(cmd *string)
	PreProcessing  func(cmd *string, u *User)
	PostProcessing func(in *Msg)
	PreResponse    func(in *Msg, resp *string)
}
func (p *Plugin) Schedule(in *Msg, content string, sendat time.Time) error {
	if sendat.Before(time.Now()) {
		return errors.New("cannot schedule time in the past")
	}
	q := `INSERT INTO scheduledevents (content, flexid, flexidtype, sendat,
		pluginname)
	      VALUES ($1, $2, $3, $4, $5)`
	_, err := p.DB.Exec(q, content, in.User.FlexID, in.User.FlexIDType, sendat, p.Config.Name)
	return err
}
func (p *Plugin) run(in *Msg) (resp string, stateMachineAnswered bool) {
	resp = p.Keywords.handle(in)
	if len(resp) == 0 {
		sm := &StateMachine{}
		*sm = *p.SM
		states := p.SetBranches(in)
		if states != nil {
			sm.SetStates(states)
		}
		sm.LoadState(in)
		if sm.state < len(sm.Handlers) {
			resp = sm.Next(in)
			stateMachineAnswered = true
		}
	}
	return resp, stateMachineAnswered
}
func CallPlugin(p *Plugin, in *Msg, followup bool) (resp string,
	stateMachineAnswered bool) {
	if p == nil {
		return "", false
	}
	if !followup {
		p.SM.Reset(in)
	}
	return p.run(in)
}
func (p *Plugin) GetMemory(in *Msg, k string) Memory {
	var buf []byte
	var err error
	if in.User.ID > 0 {
		if k == StateKey || k == stateEnteredKey {
			q := `SELECT value FROM states
			      WHERE userid=$1 AND key=$2 AND pluginname=$3`
			err = p.DB.Get(&buf, q, in.User.ID, k, p.Config.Name)
		} else {
			q := `SELECT value FROM states
			      WHERE userid=$1 AND key=$2`
			err = p.DB.Get(&buf, q, in.User.ID, k)
		}
	} else {
		if k == StateKey || k == stateEnteredKey {
			q := `SELECT value FROM states
			      WHERE flexid=$1 AND flexidtype=$2 AND key=$3 AND pluginname=$4`
			err = p.DB.Get(&buf, q, in.User.FlexID,
				in.User.FlexIDType, k, p.Config.Name)
		} else {
			q := `SELECT value FROM states
			      WHERE flexid=$1 AND flexidtype=$2 AND key=$3`
			err = p.DB.Get(&buf, q, in.User.FlexID,
				in.User.FlexIDType, k)
		}
	}
	if err == sql.ErrNoRows {
		return Memory{Key: k, Val: json.RawMessage{}, log: p.Log}
	}
	if err != nil {
		p.Log.Infof("could not get memory for key %s. %s", k,
			err.Error())
		return Memory{Key: k, Val: json.RawMessage{}, log: p.Log}
	}
	return Memory{Key: k, Val: buf, log: p.Log}
}
func (p *Plugin) SetMemory(in *Msg, k string, v interface{}) {
	b, err := json.Marshal(v)
	if err != nil {
		p.Log.Infof("could not marshal memory interface to json at %s. %s",
			k, err.Error())
		return
	}
	p.Log.Debug("setting memory for", k, "to", string(b))
	if in.User.ID > 0 {
		q := `INSERT INTO states (key, value, pluginname, userid)
		      VALUES ($1, $2, $3, $4)
		      ON CONFLICT (userid, pluginname, key)
		      DO UPDATE SET value=$2`
		_, err = p.DB.Exec(q, k, b, p.Config.Name, in.User.ID)
	} else {
		q := `INSERT INTO states
		      (key, value, pluginname, flexid, flexidtype)
		      VALUES ($1, $2, $3, $4, $5)
		      ON CONFLICT (flexid, flexidtype, pluginname, key)
		      DO UPDATE SET value=$2`
		_, err = p.DB.Exec(q, k, b, p.Config.Name, in.User.FlexID,
			in.User.FlexIDType)
	}
	if err != nil {
		p.Log.Infof("could not set memory at %s to %s. %s", k, v,
			err.Error())
		return
	}
}