Skip to content
17 changes: 14 additions & 3 deletions cmd/picoclaw/cmd_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ import (
"github.com/sipeed/picoclaw/pkg/agent"
"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
_ "github.com/sipeed/picoclaw/pkg/channels/dingtalk"
dch "github.com/sipeed/picoclaw/pkg/channels/discord"
_ "github.com/sipeed/picoclaw/pkg/channels/feishu"
_ "github.com/sipeed/picoclaw/pkg/channels/line"
_ "github.com/sipeed/picoclaw/pkg/channels/maixcam"
_ "github.com/sipeed/picoclaw/pkg/channels/onebot"
_ "github.com/sipeed/picoclaw/pkg/channels/qq"
slackch "github.com/sipeed/picoclaw/pkg/channels/slack"
tgramch "github.com/sipeed/picoclaw/pkg/channels/telegram"
_ "github.com/sipeed/picoclaw/pkg/channels/wecom"
_ "github.com/sipeed/picoclaw/pkg/channels/whatsapp"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/cron"
"github.com/sipeed/picoclaw/pkg/devices"
Expand Down Expand Up @@ -128,19 +139,19 @@ func gatewayCmd() {

if transcriber != nil {
if telegramChannel, ok := channelManager.GetChannel("telegram"); ok {
if tc, ok := telegramChannel.(*channels.TelegramChannel); ok {
if tc, ok := telegramChannel.(*tgramch.TelegramChannel); ok {
tc.SetTranscriber(transcriber)
logger.InfoC("voice", "Groq transcription attached to Telegram channel")
}
}
if discordChannel, ok := channelManager.GetChannel("discord"); ok {
if dc, ok := discordChannel.(*channels.DiscordChannel); ok {
if dc, ok := discordChannel.(*dch.DiscordChannel); ok {
dc.SetTranscriber(transcriber)
logger.InfoC("voice", "Groq transcription attached to Discord channel")
}
}
if slackChannel, ok := channelManager.GetChannel("slack"); ok {
if sc, ok := slackChannel.(*channels.SlackChannel); ok {
if sc, ok := slackChannel.(*slackch.SlackChannel); ok {
sc.SetTranscriber(transcriber)
logger.InfoC("voice", "Groq transcription attached to Slack channel")
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/channels/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package channels
import (
"context"
"strings"
"sync/atomic"

"github.com/sipeed/picoclaw/pkg/bus"
)
Expand All @@ -19,7 +20,7 @@ type Channel interface {
type BaseChannel struct {
config any
bus *bus.MessageBus
running bool
running atomic.Bool
name string
allowList []string
}
Expand All @@ -30,7 +31,6 @@ func NewBaseChannel(name string, config any, bus *bus.MessageBus, allowList []st
bus: bus,
name: name,
allowList: allowList,
running: false,
}
}

Expand All @@ -39,7 +39,7 @@ func (c *BaseChannel) Name() string {
}

func (c *BaseChannel) IsRunning() bool {
return c.running
return c.running.Load()
}

func (c *BaseChannel) IsAllowed(senderID string) bool {
Expand Down Expand Up @@ -98,6 +98,6 @@ func (c *BaseChannel) HandleMessage(senderID, chatID, content string, media []st
c.bus.PublishInbound(msg)
}

func (c *BaseChannel) setRunning(running bool) {
c.running = running
func (c *BaseChannel) SetRunning(running bool) {
c.running.Store(running)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// PicoClaw - Ultra-lightweight personal AI agent
// DingTalk channel implementation using Stream Mode

package channels
package dingtalk

import (
"context"
Expand All @@ -12,6 +12,7 @@ import (
"github.com/open-dingtalk/dingtalk-stream-sdk-go/client"

"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/logger"
"github.com/sipeed/picoclaw/pkg/utils"
Expand All @@ -20,7 +21,7 @@ import (
// DingTalkChannel implements the Channel interface for DingTalk (ι’‰ι’‰)
// It uses WebSocket for receiving messages via stream mode and API for sending
type DingTalkChannel struct {
*BaseChannel
*channels.BaseChannel
config config.DingTalkConfig
clientID string
clientSecret string
Expand All @@ -37,7 +38,7 @@ func NewDingTalkChannel(cfg config.DingTalkConfig, messageBus *bus.MessageBus) (
return nil, fmt.Errorf("dingtalk client_id and client_secret are required")
}

base := NewBaseChannel("dingtalk", cfg, messageBus, cfg.AllowFrom)
base := channels.NewBaseChannel("dingtalk", cfg, messageBus, cfg.AllowFrom)

return &DingTalkChannel{
BaseChannel: base,
Expand Down Expand Up @@ -70,7 +71,7 @@ func (c *DingTalkChannel) Start(ctx context.Context) error {
return fmt.Errorf("failed to start stream client: %w", err)
}

c.setRunning(true)
c.SetRunning(true)
logger.InfoC("dingtalk", "DingTalk channel started (Stream Mode)")
return nil
}
Expand All @@ -87,7 +88,7 @@ func (c *DingTalkChannel) Stop(ctx context.Context) error {
c.streamClient.Close()
}

c.setRunning(false)
c.SetRunning(false)
logger.InfoC("dingtalk", "DingTalk channel stopped")
return nil
}
Expand Down
13 changes: 13 additions & 0 deletions pkg/channels/dingtalk/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dingtalk

import (
"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
)

func init() {
channels.RegisterFactory("dingtalk", func(cfg *config.Config, b *bus.MessageBus) (channels.Channel, error) {
return NewDingTalkChannel(cfg.Channels.DingTalk, b)
})
}
11 changes: 6 additions & 5 deletions pkg/channels/discord.go β†’ pkg/channels/discord/discord.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package channels
package discord

import (
"context"
Expand All @@ -11,6 +11,7 @@ import (
"github.com/bwmarrin/discordgo"

"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/logger"
"github.com/sipeed/picoclaw/pkg/utils"
Expand All @@ -23,7 +24,7 @@ const (
)

type DiscordChannel struct {
*BaseChannel
*channels.BaseChannel
session *discordgo.Session
config config.DiscordConfig
transcriber *voice.GroqTranscriber
Expand All @@ -39,7 +40,7 @@ func NewDiscordChannel(cfg config.DiscordConfig, bus *bus.MessageBus) (*DiscordC
return nil, fmt.Errorf("failed to create discord session: %w", err)
}

base := NewBaseChannel("discord", cfg, bus, cfg.AllowFrom)
base := channels.NewBaseChannel("discord", cfg, bus, cfg.AllowFrom)

return &DiscordChannel{
BaseChannel: base,
Expand Down Expand Up @@ -80,7 +81,7 @@ func (c *DiscordChannel) Start(ctx context.Context) error {
return fmt.Errorf("failed to open discord session: %w", err)
}

c.setRunning(true)
c.SetRunning(true)

logger.InfoCF("discord", "Discord bot connected", map[string]any{
"username": botUser.Username,
Expand All @@ -92,7 +93,7 @@ func (c *DiscordChannel) Start(ctx context.Context) error {

func (c *DiscordChannel) Stop(ctx context.Context) error {
logger.InfoC("discord", "Stopping Discord bot")
c.setRunning(false)
c.SetRunning(false)

// Stop all typing goroutines before closing session
c.typingMu.Lock()
Expand Down
13 changes: 13 additions & 0 deletions pkg/channels/discord/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package discord

import (
"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
)

func init() {
channels.RegisterFactory("discord", func(cfg *config.Config, b *bus.MessageBus) (channels.Channel, error) {
return NewDiscordChannel(cfg.Channels.Discord, b)
})
}
9 changes: 9 additions & 0 deletions pkg/channels/feishu/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package feishu

// stringValue safely dereferences a *string pointer.
func stringValue(v *string) string {
if v == nil {
return ""
}
return *v
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
//go:build !amd64 && !arm64 && !riscv64 && !mips64 && !ppc64

package channels
package feishu

import (
"context"
"errors"

"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
)

// FeishuChannel is a stub implementation for 32-bit architectures
type FeishuChannel struct {
*BaseChannel
*channels.BaseChannel
}

// NewFeishuChannel returns an error on 32-bit architectures where the Feishu SDK is not supported
func NewFeishuChannel(cfg config.FeishuConfig, bus *bus.MessageBus) (*FeishuChannel, error) {
return nil, errors.New(
"feishu channel is not supported on 32-bit architectures (armv7l, 386, etc.). Please use a 64-bit system or disable feishu in your config",
)
return nil, errors.New("feishu channel is not supported on 32-bit architectures (armv7l, 386, etc.). Please use a 64-bit system or disable feishu in your config")
}

// Start is a stub method to satisfy the Channel interface
Expand Down
18 changes: 6 additions & 12 deletions pkg/channels/feishu_64.go β†’ pkg/channels/feishu/feishu_64.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//go:build amd64 || arm64 || riscv64 || mips64 || ppc64

package channels
package feishu

import (
"context"
Expand All @@ -15,13 +15,14 @@ import (
larkws "github.com/larksuite/oapi-sdk-go/v3/ws"

"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/logger"
"github.com/sipeed/picoclaw/pkg/utils"
)

type FeishuChannel struct {
*BaseChannel
*channels.BaseChannel
config config.FeishuConfig
client *lark.Client
wsClient *larkws.Client
Expand All @@ -31,7 +32,7 @@ type FeishuChannel struct {
}

func NewFeishuChannel(cfg config.FeishuConfig, bus *bus.MessageBus) (*FeishuChannel, error) {
base := NewBaseChannel("feishu", cfg, bus, cfg.AllowFrom)
base := channels.NewBaseChannel("feishu", cfg, bus, cfg.AllowFrom)

return &FeishuChannel{
BaseChannel: base,
Expand Down Expand Up @@ -60,7 +61,7 @@ func (c *FeishuChannel) Start(ctx context.Context) error {
wsClient := c.wsClient
c.mu.Unlock()

c.setRunning(true)
c.SetRunning(true)
logger.InfoC("feishu", "Feishu channel started (websocket mode)")

go func() {
Expand All @@ -83,7 +84,7 @@ func (c *FeishuChannel) Stop(ctx context.Context) error {
c.wsClient = nil
c.mu.Unlock()

c.setRunning(false)
c.SetRunning(false)
logger.InfoC("feishu", "Feishu channel stopped")
return nil
}
Expand Down Expand Up @@ -218,10 +219,3 @@ func extractFeishuMessageContent(message *larkim.EventMessage) string {

return *message.Content
}

func stringValue(v *string) string {
if v == nil {
return ""
}
return *v
}
13 changes: 13 additions & 0 deletions pkg/channels/feishu/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package feishu

import (
"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
)

func init() {
channels.RegisterFactory("feishu", func(cfg *config.Config, b *bus.MessageBus) (channels.Channel, error) {
return NewFeishuChannel(cfg.Channels.Feishu, b)
})
}
13 changes: 13 additions & 0 deletions pkg/channels/line/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package line

import (
"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
)

func init() {
channels.RegisterFactory("line", func(cfg *config.Config, b *bus.MessageBus) (channels.Channel, error) {
return NewLINEChannel(cfg.Channels.LINE, b)
})
}
11 changes: 6 additions & 5 deletions pkg/channels/line.go β†’ pkg/channels/line/line.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package channels
package line

import (
"bytes"
Expand All @@ -16,6 +16,7 @@ import (
"time"

"github.com/sipeed/picoclaw/pkg/bus"
"github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/logger"
"github.com/sipeed/picoclaw/pkg/utils"
Expand All @@ -41,7 +42,7 @@ type replyTokenEntry struct {
// using the LINE Messaging API with HTTP webhook for receiving messages
// and REST API for sending messages.
type LINEChannel struct {
*BaseChannel
*channels.BaseChannel
config config.LINEConfig
httpServer *http.Server
botUserID string // Bot's user ID
Expand All @@ -59,7 +60,7 @@ func NewLINEChannel(cfg config.LINEConfig, messageBus *bus.MessageBus) (*LINECha
return nil, fmt.Errorf("line channel_secret and channel_access_token are required")
}

base := NewBaseChannel("line", cfg, messageBus, cfg.AllowFrom)
base := channels.NewBaseChannel("line", cfg, messageBus, cfg.AllowFrom)

return &LINEChannel{
BaseChannel: base,
Expand Down Expand Up @@ -111,7 +112,7 @@ func (c *LINEChannel) Start(ctx context.Context) error {
}
}()

c.setRunning(true)
c.SetRunning(true)
logger.InfoC("line", "LINE channel started (Webhook Mode)")
return nil
}
Expand Down Expand Up @@ -168,7 +169,7 @@ func (c *LINEChannel) Stop(ctx context.Context) error {
}
}

c.setRunning(false)
c.SetRunning(false)
logger.InfoC("line", "LINE channel stopped")
return nil
}
Expand Down
Loading