mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: channel gateway implementation and multi-bot refactor
- Refactor channel manager with support for Sender/Receiver interfaces and hot-swappable adapters. - Implement identity routing and pre-authentication logic for inbound messages. - Update database schema to support bot pre-auth keys and extended channel session metadata. - Add Telegram and Feishu channel configuration and adapter enhancements. - Update Swagger documentation and internal handlers for channel management. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,8 +6,8 @@ DROP TABLE IF EXISTS container_versions;
|
||||
DROP TABLE IF EXISTS snapshots;
|
||||
DROP TABLE IF EXISTS containers;
|
||||
DROP TABLE IF EXISTS channel_sessions;
|
||||
DROP TABLE IF EXISTS contact_bind_tokens;
|
||||
DROP TABLE IF EXISTS contact_channels;
|
||||
DROP TABLE IF EXISTS bot_preauth_keys;
|
||||
DROP TABLE IF EXISTS contacts;
|
||||
DROP TABLE IF EXISTS bot_channel_configs;
|
||||
DROP TABLE IF EXISTS user_channel_bindings;
|
||||
|
||||
@@ -95,7 +95,7 @@ CREATE INDEX IF NOT EXISTS idx_bot_members_user_id ON bot_members(user_id);
|
||||
CREATE TABLE IF NOT EXISTS bot_settings (
|
||||
bot_id UUID PRIMARY KEY REFERENCES bots(id) ON DELETE CASCADE,
|
||||
max_context_load_time INTEGER NOT NULL DEFAULT 1440,
|
||||
language TEXT NOT NULL DEFAULT 'Same as user input',
|
||||
language TEXT NOT NULL DEFAULT 'auto',
|
||||
allow_guest BOOLEAN NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
@@ -125,6 +125,7 @@ CREATE TABLE IF NOT EXISTS history (
|
||||
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
|
||||
session_id TEXT NOT NULL,
|
||||
messages JSONB NOT NULL,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
skills TEXT[] NOT NULL DEFAULT '{}'::text[],
|
||||
timestamp TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
@@ -187,6 +188,20 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_contacts_bot_user_unique
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contacts_bot_id ON contacts(bot_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bot_preauth_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
|
||||
token TEXT NOT NULL,
|
||||
issued_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
expires_at TIMESTAMPTZ,
|
||||
used_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT bot_preauth_keys_unique UNIQUE (token)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bot_preauth_keys_bot_id ON bot_preauth_keys(bot_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_bot_preauth_keys_expires ON bot_preauth_keys(expires_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS contact_channels (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
|
||||
@@ -202,23 +217,6 @@ CREATE TABLE IF NOT EXISTS contact_channels (
|
||||
CREATE INDEX IF NOT EXISTS idx_contact_channels_contact_id ON contact_channels(contact_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_contact_channels_platform_external ON contact_channels(platform, external_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS contact_bind_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
|
||||
contact_id UUID NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
||||
token TEXT NOT NULL,
|
||||
target_platform TEXT,
|
||||
target_external_id TEXT,
|
||||
issued_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
used_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT contact_bind_tokens_unique UNIQUE (token)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_contact_bind_tokens_contact_id ON contact_bind_tokens(contact_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_contact_bind_tokens_expires ON contact_bind_tokens(expires_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS channel_sessions (
|
||||
session_id TEXT PRIMARY KEY,
|
||||
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
|
||||
@@ -226,6 +224,9 @@ CREATE TABLE IF NOT EXISTS channel_sessions (
|
||||
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
||||
contact_id UUID REFERENCES contacts(id) ON DELETE SET NULL,
|
||||
platform TEXT NOT NULL,
|
||||
reply_target TEXT,
|
||||
thread_id TEXT,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
@@ -325,6 +326,9 @@ CREATE INDEX IF NOT EXISTS idx_subagents_deleted ON subagents(deleted);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_settings (
|
||||
user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
||||
chat_model_id TEXT,
|
||||
memory_model_id TEXT,
|
||||
embedding_model_id TEXT,
|
||||
max_context_load_time INTEGER NOT NULL DEFAULT 1440,
|
||||
language TEXT NOT NULL DEFAULT 'Same as user input'
|
||||
language TEXT NOT NULL DEFAULT 'auto'
|
||||
);
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
-- name: GetBotChannelConfig :one
|
||||
SELECT id, bot_id, channel_type, credentials, external_identity, self_identity, routing, capabilities, status, verified_at, created_at, updated_at
|
||||
FROM bot_channel_configs
|
||||
WHERE bot_id = $1 AND channel_type = $2
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetBotChannelConfigByExternalIdentity :one
|
||||
SELECT id, bot_id, channel_type, credentials, external_identity, self_identity, routing, capabilities, status, verified_at, created_at, updated_at
|
||||
FROM bot_channel_configs
|
||||
WHERE channel_type = $1 AND external_identity = $2
|
||||
LIMIT 1;
|
||||
|
||||
-- name: UpsertBotChannelConfig :one
|
||||
INSERT INTO bot_channel_configs (
|
||||
bot_id, channel_type, credentials, external_identity, self_identity, routing, capabilities, status, verified_at
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
ON CONFLICT (bot_id, channel_type)
|
||||
DO UPDATE SET
|
||||
credentials = EXCLUDED.credentials,
|
||||
external_identity = EXCLUDED.external_identity,
|
||||
self_identity = EXCLUDED.self_identity,
|
||||
routing = EXCLUDED.routing,
|
||||
capabilities = EXCLUDED.capabilities,
|
||||
status = EXCLUDED.status,
|
||||
verified_at = EXCLUDED.verified_at,
|
||||
updated_at = now()
|
||||
RETURNING id, bot_id, channel_type, credentials, external_identity, self_identity, routing, capabilities, status, verified_at, created_at, updated_at;
|
||||
|
||||
-- name: ListBotChannelConfigsByType :many
|
||||
SELECT id, bot_id, channel_type, credentials, external_identity, self_identity, routing, capabilities, status, verified_at, created_at, updated_at
|
||||
FROM bot_channel_configs
|
||||
WHERE channel_type = $1
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: GetUserChannelBinding :one
|
||||
SELECT id, user_id, channel_type, config, created_at, updated_at
|
||||
FROM user_channel_bindings
|
||||
WHERE user_id = $1 AND channel_type = $2
|
||||
LIMIT 1;
|
||||
|
||||
-- name: UpsertUserChannelBinding :one
|
||||
INSERT INTO user_channel_bindings (user_id, channel_type, config)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (user_id, channel_type)
|
||||
DO UPDATE SET
|
||||
config = EXCLUDED.config,
|
||||
updated_at = now()
|
||||
RETURNING id, user_id, channel_type, config, created_at, updated_at;
|
||||
|
||||
-- name: ListUserChannelBindingsByType :many
|
||||
SELECT id, user_id, channel_type, config, created_at, updated_at
|
||||
FROM user_channel_bindings
|
||||
WHERE channel_type = $1
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: GetChannelSessionByID :one
|
||||
SELECT session_id, bot_id, channel_config_id, user_id, contact_id, platform, reply_target, thread_id, metadata, created_at, updated_at
|
||||
FROM channel_sessions
|
||||
WHERE session_id = $1
|
||||
LIMIT 1;
|
||||
|
||||
-- name: ListChannelSessionsByBotPlatform :many
|
||||
SELECT session_id, bot_id, channel_config_id, user_id, contact_id, platform, reply_target, thread_id, metadata, created_at, updated_at
|
||||
FROM channel_sessions
|
||||
WHERE bot_id = $1 AND platform = $2
|
||||
ORDER BY updated_at DESC;
|
||||
|
||||
-- name: UpsertChannelSession :one
|
||||
INSERT INTO channel_sessions (session_id, bot_id, channel_config_id, user_id, contact_id, platform, reply_target, thread_id, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
ON CONFLICT (session_id)
|
||||
DO UPDATE SET
|
||||
bot_id = EXCLUDED.bot_id,
|
||||
channel_config_id = EXCLUDED.channel_config_id,
|
||||
user_id = EXCLUDED.user_id,
|
||||
contact_id = EXCLUDED.contact_id,
|
||||
platform = EXCLUDED.platform,
|
||||
reply_target = EXCLUDED.reply_target,
|
||||
thread_id = EXCLUDED.thread_id,
|
||||
metadata = EXCLUDED.metadata,
|
||||
updated_at = now()
|
||||
RETURNING session_id, bot_id, channel_config_id, user_id, contact_id, platform, reply_target, thread_id, metadata, created_at, updated_at;
|
||||
|
||||
-- name: DeleteChannelSession :exec
|
||||
DELETE FROM channel_sessions
|
||||
WHERE session_id = $1;
|
||||
@@ -0,0 +1,76 @@
|
||||
-- name: CreateContact :one
|
||||
INSERT INTO contacts (bot_id, user_id, display_name, alias, tags, status, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id, bot_id, user_id, display_name, alias, tags, status, metadata, created_at, updated_at;
|
||||
|
||||
-- name: GetContactByID :one
|
||||
SELECT id, bot_id, user_id, display_name, alias, tags, status, metadata, created_at, updated_at
|
||||
FROM contacts
|
||||
WHERE id = $1
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetContactByUserID :one
|
||||
SELECT id, bot_id, user_id, display_name, alias, tags, status, metadata, created_at, updated_at
|
||||
FROM contacts
|
||||
WHERE bot_id = $1 AND user_id = $2
|
||||
LIMIT 1;
|
||||
|
||||
-- name: ListContactsByBot :many
|
||||
SELECT id, bot_id, user_id, display_name, alias, tags, status, metadata, created_at, updated_at
|
||||
FROM contacts
|
||||
WHERE bot_id = $1
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: SearchContacts :many
|
||||
SELECT id, bot_id, user_id, display_name, alias, tags, status, metadata, created_at, updated_at
|
||||
FROM contacts
|
||||
WHERE bot_id = $1
|
||||
AND (
|
||||
display_name ILIKE sqlc.arg(query)
|
||||
OR alias ILIKE sqlc.arg(query)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM unnest(tags) AS tag WHERE tag ILIKE sqlc.arg(query)
|
||||
)
|
||||
)
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: UpdateContact :one
|
||||
UPDATE contacts
|
||||
SET display_name = COALESCE(sqlc.narg(display_name), display_name),
|
||||
alias = COALESCE(sqlc.narg(alias), alias),
|
||||
tags = COALESCE(sqlc.narg(tags), tags),
|
||||
status = COALESCE(NULLIF(sqlc.arg(status)::text, ''), status),
|
||||
metadata = COALESCE(sqlc.narg(metadata), metadata),
|
||||
updated_at = now()
|
||||
WHERE id = sqlc.arg(id)
|
||||
RETURNING id, bot_id, user_id, display_name, alias, tags, status, metadata, created_at, updated_at;
|
||||
|
||||
-- name: UpdateContactUser :one
|
||||
UPDATE contacts
|
||||
SET user_id = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1
|
||||
RETURNING id, bot_id, user_id, display_name, alias, tags, status, metadata, created_at, updated_at;
|
||||
|
||||
-- name: UpsertContactChannel :one
|
||||
INSERT INTO contact_channels (bot_id, contact_id, platform, external_id, metadata)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (bot_id, platform, external_id)
|
||||
DO UPDATE SET
|
||||
contact_id = EXCLUDED.contact_id,
|
||||
metadata = EXCLUDED.metadata,
|
||||
updated_at = now()
|
||||
RETURNING id, bot_id, contact_id, platform, external_id, metadata, created_at, updated_at;
|
||||
|
||||
-- name: GetContactChannelByIdentity :one
|
||||
SELECT id, bot_id, contact_id, platform, external_id, metadata, created_at, updated_at
|
||||
FROM contact_channels
|
||||
WHERE bot_id = $1 AND platform = $2 AND external_id = $3
|
||||
LIMIT 1;
|
||||
|
||||
-- name: ListContactChannelsByContact :many
|
||||
SELECT id, bot_id, contact_id, platform, external_id, metadata, created_at, updated_at
|
||||
FROM contact_channels
|
||||
WHERE contact_id = $1
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
@@ -31,3 +31,6 @@ ON CONFLICT (container_id) DO UPDATE SET
|
||||
|
||||
-- name: GetContainerByContainerID :one
|
||||
SELECT * FROM containers WHERE container_id = sqlc.arg(container_id);
|
||||
|
||||
-- name: GetContainerByBotID :one
|
||||
SELECT * FROM containers WHERE bot_id = sqlc.arg(bot_id) ORDER BY updated_at DESC LIMIT 1;
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
-- name: CreateHistory :one
|
||||
INSERT INTO history (bot_id, session_id, messages, skills, timestamp)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, bot_id, session_id, messages, skills, timestamp;
|
||||
INSERT INTO history (bot_id, session_id, messages, metadata, skills, timestamp)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, bot_id, session_id, messages, metadata, skills, timestamp;
|
||||
|
||||
-- name: ListHistoryByBotSessionSince :many
|
||||
SELECT id, bot_id, session_id, messages, skills, timestamp
|
||||
SELECT id, bot_id, session_id, messages, metadata, skills, timestamp
|
||||
FROM history
|
||||
WHERE bot_id = $1 AND session_id = $2 AND timestamp >= $3
|
||||
ORDER BY timestamp ASC;
|
||||
|
||||
-- name: GetHistoryByID :one
|
||||
SELECT id, bot_id, session_id, messages, skills, timestamp
|
||||
SELECT id, bot_id, session_id, messages, metadata, skills, timestamp
|
||||
FROM history
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: ListHistoryByBotSession :many
|
||||
SELECT id, bot_id, session_id, messages, skills, timestamp
|
||||
SELECT id, bot_id, session_id, messages, metadata, skills, timestamp
|
||||
FROM history
|
||||
WHERE bot_id = $1 AND session_id = $2
|
||||
ORDER BY timestamp DESC
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
-- name: CreateBotPreauthKey :one
|
||||
INSERT INTO bot_preauth_keys (bot_id, token, issued_by_user_id, expires_at)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, bot_id, token, issued_by_user_id, expires_at, used_at, created_at;
|
||||
|
||||
-- name: GetBotPreauthKey :one
|
||||
SELECT id, bot_id, token, issued_by_user_id, expires_at, used_at, created_at
|
||||
FROM bot_preauth_keys
|
||||
WHERE token = $1
|
||||
LIMIT 1;
|
||||
|
||||
-- name: MarkBotPreauthKeyUsed :one
|
||||
UPDATE bot_preauth_keys
|
||||
SET used_at = now()
|
||||
WHERE id = $1
|
||||
RETURNING id, bot_id, token, issued_by_user_id, expires_at, used_at, created_at;
|
||||
Reference in New Issue
Block a user