Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/web/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -668,13 +668,15 @@
"types": {
"feishu": "Feishu",
"discord": "Discord",
"qq": "QQ",
"telegram": "Telegram",
"web": "Web",
"local": "Local"
},
"typesShort": {
"feishu": "FS",
"discord": "DC",
"qq": "QQ",
"telegram": "TG",
"web": "Web",
"local": "CLI"
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -664,13 +664,15 @@
"types": {
"feishu": "飞书",
"discord": "Discord",
"qq": "QQ",
"telegram": "Telegram",
"web": "Web",
"local": "本地"
},
"typesShort": {
"feishu": "",
"discord": "DC",
"qq": "QQ",
"telegram": "TG",
"web": "Web",
"local": "CLI"
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/pages/bots/components/bot-channels.vue
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ function addChannel(type: string) {

function channelIcon(type: string): string {
const icons: Record<string, string> = {
qq: 'QQ',
telegram: 'TG',
feishu: '飞',
}
Expand All @@ -220,6 +221,7 @@ function channelIcon(type: string): string {

function channelBadgeClass(type: string): string {
const classes: Record<string, string> = {
qq: 'bg-sky-100 text-sky-700 dark:bg-sky-900 dark:text-sky-300',
telegram: 'bg-blue-100 text-blue-700 dark:bg-blue-900 dark:text-blue-300',
feishu: 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900 dark:text-indigo-300',
}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/pages/settings/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ function platformLabel(platformKey: string): string {
}
const platformOptions = computed(() => {
const options = new Set<string>(['telegram', 'feishu', 'discord'])
const options = new Set<string>(['telegram', 'feishu', 'discord', 'qq'])
for (const identity of identities.value) {
const platform = identity.channel.trim()
if (platform) {
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/utils/channel-icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const LOCAL_CHANNEL_IMAGES: Record<string, string> = {
}

const CHANNEL_ICONS: Record<string, [string, string]> = {
qq: ['fab', 'qq'],
telegram: ['fab', 'telegram'],
feishu: ['fas', 'comment-dots'],
web: ['fas', 'globe'],
Expand Down
16 changes: 16 additions & 0 deletions cmd/agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/memohai/memoh/internal/channel/adapters/discord"
"github.com/memohai/memoh/internal/channel/adapters/feishu"
"github.com/memohai/memoh/internal/channel/adapters/local"
"github.com/memohai/memoh/internal/channel/adapters/qq"
"github.com/memohai/memoh/internal/channel/adapters/telegram"
"github.com/memohai/memoh/internal/channel/identities"
"github.com/memohai/memoh/internal/channel/inbound"
Expand Down Expand Up @@ -391,6 +392,10 @@ func provideChannelRegistry(log *slog.Logger, hub *local.RouteHub, mediaService
discordAdapter.SetAssetOpener(mediaService)
registry.MustRegister(discordAdapter)

qqAdapter := qq.NewQQAdapter(log)
qqAdapter.SetAssetOpener(mediaService)
registry.MustRegister(qqAdapter)

registry.MustRegister(feishu.NewFeishuAdapter(log))
registry.MustRegister(local.NewCLIAdapter(hub))
registry.MustRegister(local.NewWebAdapter(hub))
Expand All @@ -413,6 +418,17 @@ func provideChannelRouter(
inboxService *inbox.Service,
rc *boot.RuntimeConfig,
) *inbound.ChannelInboundProcessor {
adapter, ok := registry.Get(qq.Type)
if !ok {
panic("qq adapter not registered")
}
qqAdapter, ok := adapter.(*qq.QQAdapter)
if !ok {
panic("qq adapter has unexpected type")
}
qqAdapter.SetChannelIdentityResolver(identityService)
qqAdapter.SetRouteResolver(routeService)

processor := inbound.NewChannelInboundProcessor(log, registry, routeService, msgService, resolver, identityService, botService, policyService, preauthService, bindService, rc.JwtSecret, 5*time.Minute)
processor.SetMediaService(mediaService)
processor.SetStreamObserver(local.NewRouteHubBroadcaster(hub))
Expand Down
3 changes: 3 additions & 0 deletions internal/attachment/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func NormalizeMime(raw string) string {
if idx := strings.Index(mime, ";"); idx >= 0 {
mime = strings.TrimSpace(mime[:idx])
}
if !strings.Contains(mime, "/") {
return ""
}
return mime
}

Expand Down
6 changes: 6 additions & 0 deletions internal/attachment/normalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func TestNormalizeMime(t *testing.T) {
if got != "image/jpeg" {
t.Fatalf("NormalizeMime unexpected result: %q", got)
}
if got := NormalizeMime("file"); got != "" {
t.Fatalf("NormalizeMime should drop invalid mime token, got %q", got)
}
}

func TestMimeFromDataURL(t *testing.T) {
Expand All @@ -67,6 +70,9 @@ func TestResolveMime(t *testing.T) {
if got := ResolveMime(media.MediaTypeFile, "application/octet-stream", "application/pdf"); got != "application/pdf" {
t.Fatalf("ResolveMime file unexpected result: %q", got)
}
if got := ResolveMime(media.MediaTypeFile, "file", "text/plain"); got != "text/plain" {
t.Fatalf("ResolveMime should prefer sniffed mime for invalid source token, got %q", got)
}
if got := ResolveMime(media.MediaTypeImage, "", ""); got != "application/octet-stream" {
t.Fatalf("ResolveMime empty unexpected result: %q", got)
}
Expand Down
Loading