Initial bot functionality

* The bot now properly joins the management room
* The management room is persisted in the database
* Welcome/help messages are sent in the management room
This commit is contained in:
Gary Kramlich
2021-12-30 09:33:06 -06:00
parent 78ab3d3804
commit 456a15ba56
19 changed files with 859 additions and 16 deletions

View File

@@ -15,6 +15,10 @@ type Database struct {
*sql.DB
log log.Logger
dialect string
User *UserQuery
Portal *PortalQuery
Puppet *PuppetQuery
}
func New(dbType, uri string, maxOpenConns, maxIdleConns int, baseLog log.Logger) (*Database, error) {
@@ -42,5 +46,20 @@ func New(dbType, uri string, maxOpenConns, maxIdleConns int, baseLog log.Logger)
dialect: dbType,
}
db.User = &UserQuery{
db: db,
log: db.log.Sub("User"),
}
db.Portal = &PortalQuery{
db: db,
log: db.log.Sub("Portal"),
}
db.Puppet = &PuppetQuery{
db: db,
log: db.log.Sub("Puppet"),
}
return db, nil
}

View File

@@ -1,11 +1,32 @@
CREATE TABLE IF NOT EXISTS portal (
did text,
receiver text,
mxid text UNIQUE,
id TEXT,
receiver TEXT,
mxid TEXT UNIQUE,
name text NOT NULL,
topic text NOT NULL,
avatar text NOT NULL,
name TEXT NOT NULL,
topic TEXT NOT NULL,
PRIMARY KEY (did, receiver)
avatar TEXT NOT NULL,
avatar_url TEXT NOT NULL,
PRIMARY KEY (id, receiver)
);
CREATE TABLE IF NOT EXISTS puppet (
id TEXT PRIMARY KEY,
displayname TEXT,
avatar TEXT,
avatar_url TEXT,
enable_presence BOOLEAN NOT NULL DEFAULT true
);
CREATE TABLE IF NOT EXISTS user (
mxid TEXT PRIMARY KEY,
id TEXT UNIQUE,
management_room TEXT,
token TEXT
);

53
database/portal.go Normal file
View File

@@ -0,0 +1,53 @@
package database
import (
"database/sql"
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/id"
)
type Portal struct {
db *Database
log log.Logger
Key PortalKey
MXID id.RoomID
Name string
Topic string
Avatar string
AvatarURL id.ContentURI
}
func (p *Portal) Scan(row Scannable) *Portal {
var mxid, avatarURL sql.NullString
err := row.Scan(&p.Key.ID, &p.Key.Receiver, &mxid, &p.Name, &p.Topic, &p.Avatar, &avatarURL)
if err != nil {
if err != sql.ErrNoRows {
p.log.Errorln("Database scan failed:", err)
}
return nil
}
p.MXID = id.RoomID(mxid.String)
p.AvatarURL, _ = id.ParseContentURI(avatarURL.String)
return p
}
func (p *Portal) Insert() {
query := "INSERT INTO portal" +
" (id, receiver, mxid, name, topic, avatar, avatar_url)" +
" VALUES ($1, $2, $3, $4, $5, $6, $7)"
_, err := p.db.Exec(query, p.Key.ID, p.Key.Receiver, p.MXID,
p.Name, p.Topic, p.Avatar, p.AvatarURL.String())
if err != nil {
p.log.Warnfln("Failed to insert %s: %v", p.Key, err)
}
}

13
database/portalkey.go Normal file
View File

@@ -0,0 +1,13 @@
package database
type PortalKey struct {
ID string
Receiver string
}
func (key PortalKey) String() string {
if key.Receiver == key.ID {
return key.ID
}
return key.ID + "-" + key.Receiver
}

58
database/portalquery.go Normal file
View File

@@ -0,0 +1,58 @@
package database
import (
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/id"
)
type PortalQuery struct {
db *Database
log log.Logger
}
func (pq *PortalQuery) New() *Portal {
return &Portal{
db: pq.db,
log: pq.log,
}
}
func (pq *PortalQuery) GetAll() []*Portal {
return pq.getAll("SELECT * FROM portal")
}
func (pq *PortalQuery) GetByDID(key PortalKey) *Portal {
return pq.get("SELECT * FROM portal WHERE did=$1 AND receiver=$2", key.ID, key.Receiver)
}
func (pq *PortalQuery) GetByMXID(mxid id.RoomID) *Portal {
return pq.get("SELECT * FROM portal WHERE mxid=$1", mxid)
}
func (pq *PortalQuery) GetAllByDID(did string) []*Portal {
return pq.getAll("SELECT * FROM portal WHERE did=$1", did)
}
func (pq *PortalQuery) getAll(query string, args ...interface{}) []*Portal {
rows, err := pq.db.Query(query, args...)
if err != nil || rows == nil {
return nil
}
defer rows.Close()
portals := []*Portal{}
for rows.Next() {
portals = append(portals, pq.New().Scan(rows))
}
return portals
}
func (pq *PortalQuery) get(query string, args ...interface{}) *Portal {
row := pq.db.QueryRow(query, args...)
if row == nil {
return nil
}
return pq.New().Scan(row)
}

56
database/puppet.go Normal file
View File

@@ -0,0 +1,56 @@
package database
import (
"database/sql"
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/id"
)
type Puppet struct {
db *Database
log log.Logger
ID string
DisplayName string
Avatar string
AvatarURL id.ContentURI
EnablePresence bool
}
func (p *Puppet) Scan(row Scannable) *Puppet {
var did, displayName, avatar, avatarURL sql.NullString
var enablePresence sql.NullBool
err := row.Scan(&did, &displayName, &avatar, &avatarURL, &enablePresence)
if err != nil {
if err != sql.ErrNoRows {
p.log.Errorln("Database scan failed:", err)
}
return nil
}
p.ID = did.String
p.DisplayName = displayName.String
p.Avatar = avatar.String
p.AvatarURL, _ = id.ParseContentURI(avatarURL.String)
p.EnablePresence = enablePresence.Bool
return p
}
func (p *Puppet) Insert() {
query := "INSERT INTO puppet" +
" (id, display_name, avatar, avatar_url, enable_presence)" +
" VALUES ($1, $2, $3, $4, $5)"
_, err := p.db.Exec(query, p.ID, p.DisplayName, p.Avatar,
p.AvatarURL.String(), p.EnablePresence)
if err != nil {
p.log.Warnfln("Failed to insert %s: %v", p.ID, err)
}
}

28
database/puppetquery.go Normal file
View File

@@ -0,0 +1,28 @@
package database
import (
log "maunium.net/go/maulogger/v2"
)
type PuppetQuery struct {
db *Database
log log.Logger
}
func (pq *PuppetQuery) New() *Puppet {
return &Puppet{
db: pq.db,
log: pq.log,
EnablePresence: true,
}
}
func (pq *PuppetQuery) Get(id string) *Puppet {
row := pq.db.QueryRow("SELECT id, displayname, avatar, avatar_url, enable_presence FROM puppet WHERE id=$1", id)
if row == nil {
return nil
}
return pq.New().Scan(row)
}

5
database/scannable.go Normal file
View File

@@ -0,0 +1,5 @@
package database
type Scannable interface {
Scan(...interface{}) error
}

83
database/user.go Normal file
View File

@@ -0,0 +1,83 @@
package database
import (
"database/sql"
"github.com/bwmarrin/discordgo"
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/id"
)
type User struct {
db *Database
log log.Logger
MXID id.UserID
ID string
ManagementRoom id.RoomID
Session *discordgo.Session
}
func (u *User) Scan(row Scannable) *User {
var token sql.NullString
err := row.Scan(&u.MXID, &u.ID, &u.ManagementRoom, &token)
if err != nil {
if err != sql.ErrNoRows {
u.log.Errorln("Database scan failed:", err)
}
return nil
}
if token.Valid {
session, err := discordgo.New("Bearer " + token.String)
if err != nil {
u.log.Errorln("Failed to create discord session:", err)
} else {
u.Session = session
}
}
return u
}
func (u *User) sessionNonptr() discordgo.Session {
if u.Session != nil {
return *u.Session
}
return discordgo.Session{}
}
func (u *User) Insert() {
session := u.sessionNonptr()
query := "INSERT INTO user" +
" (mxid, id, management_room, token)" +
" VALUES ($1, $2, $3, $4);"
_, err := u.db.Exec(query, u.MXID, u.ID, u.ManagementRoom,
session.Identify.Token)
if err != nil {
u.log.Warnfln("Failed to insert %s: %v", u.MXID, err)
}
}
func (u *User) Update() {
session := u.sessionNonptr()
query := "UPDATE user SET" +
" id=$1, management_room=$2, token=$3" +
" WHERE mxid=$4;"
_, err := u.db.Exec(query, u.ID, u.ManagementRoom, session.Identify.Token, u.MXID)
if err != nil {
u.log.Warnfln("Failed to update %q: %v", u.MXID, err)
}
}

27
database/userquery.go Normal file
View File

@@ -0,0 +1,27 @@
package database
import (
log "maunium.net/go/maulogger/v2"
"maunium.net/go/mautrix/id"
)
type UserQuery struct {
db *Database
log log.Logger
}
func (uq *UserQuery) New() *User {
return &User{
db: uq.db,
log: uq.log,
}
}
func (uq *UserQuery) GetByMXID(userID id.UserID) *User {
row := uq.db.QueryRow("SELECT mxid, id, management_room, token FROM user where mxid=$1", userID)
if row == nil {
return nil
}
return uq.New().Scan(row)
}