Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop #16

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ A general-purpose API gateway for microservices to provide an HTTP endpoint, nam

The project uses [gin](https://github.com/gin-gonic/gin) as the `web` framework for the public access gateway layer.

# Design Doc

- [Wiki Page](https://gamma.app/public/ChattyAI-l79uftz5bxwbdd8?mode=doc)
- [GPT API Doc](https://renaissancelabs101.notion.site/API-Access-ea86d8bd0e1345799db00bef03a92151?pvs=4)
- [Subscription Design Doc](https://renaissancelabs101.notion.site/Subscription-0ea7aa61c2514dafac72ea1764766fd0?pvs=4)
- [Comprehensive Cost Analyze Doc](https://renaissancelabs101.notion.site/Comprehensive-Cost-Analyze-of-Chatty-AI-System-b417ebe28a8542fe8bec4ee6f90438bb?pvs=4)

## Layers

The project is divided into the following layers:
Expand Down
4 changes: 3 additions & 1 deletion conf/test.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
"SUPABASEURL": "${SUPABASEURL}",
"SUPABASEKEY": "${SUPABASEKEY}",
"OPENAIAPIKEY": "${OPENAIAPIKEY}",
"DISCORDBOTTOKEN": "${DISCORDBOTTOKEN}",
"LEMONSQUEEZYAPIKEY": "${LEMONSQUEEZYAPIKEY}",
"LEMONSQUEEZYSIGNINGSECRET": "${LEMONSQUEEZYSIGNINGSECRET}",
"LEMONSQUEEZYSTOREID": "${LEMONSQUEEZYSTOREID}",
"LEMONSQUEEZYASSOCIATEDVARIANTID": "${LEMONSQUEEZYASSOCIATEDVARIANTID}",
"CLERKSECRET": "${CLERKSECRET}"
"CLERKSIGNINGSECRET": "${CLERKSIGNINGSECRET}"
}
65 changes: 65 additions & 0 deletions discord/bot/bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package bot

import (
"log"
"os"
"os/signal"
"syscall"

discord "github.com/bwmarrin/discordgo"
)

type Bot struct {
*discord.Session

Router *Router
}

func NewBot(token string) (*Bot, error) {
session, err := discord.New("Bot " + token)
if err != nil {
return nil, err
}
return &Bot{
Session: session,
Router: NewRouter(nil),
}, nil
}

func (b *Bot) Run(guildID string, removeCommands bool) {
// IntentMessageContent is required for us to have a conversation in threads without typing any commands
b.Identify.Intents = discord.MakeIntent(discord.IntentsAllWithoutPrivileged | discord.IntentMessageContent)

// Add handlers
b.AddHandler(func(s *discord.Session, r *discord.Ready) {
log.Printf("Logged in as: %v#%v", s.State.User.Username, s.State.User.Discriminator)
})
b.AddHandler(b.Router.HandleInteraction)
b.AddHandler(b.Router.HandleMessage)

// Run the bot
err := b.Open()
if err != nil {
log.Fatalf("Cannot open the session: %v", err)
}

// Sync commands
err = b.Router.Sync(b.Session, guildID)
if err != nil {
panic(err)
}

defer b.Close()

stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop

// Unregister commands if requested
if removeCommands {
log.Println("Removing commands...")
b.Router.ClearCommands(b.Session, guildID)
}

log.Println("Gracefully shutting down.")
}
67 changes: 67 additions & 0 deletions discord/bot/command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package bot

import (
discord "github.com/bwmarrin/discordgo"
)

type Handler interface {
HandleCommand(ctx *Context)
}

type HandlerFunc func(ctx *Context)

func (f HandlerFunc) HandleCommand(ctx *Context) { f(ctx) }

type MessageHandler interface {
HandleMessageCommand(ctx *MessageContext)
}

type MessageHandlerFunc func(ctx *MessageContext)

func (f MessageHandlerFunc) HandleMessageCommand(ctx *MessageContext) { f(ctx) }

type Command struct {
Name string
Description string
DMPermission bool
DefaultMemberPermissions int64
Options []*discord.ApplicationCommandOption
Type discord.ApplicationCommandType

Handler Handler
Middlewares []Handler
MessageHandler MessageHandler

SubCommands *Router
}

func (cmd Command) ApplicationCommand() *discord.ApplicationCommand {
applicationCommand := &discord.ApplicationCommand{
Name: cmd.Name,
Description: cmd.Description,
DMPermission: &cmd.DMPermission,
DefaultMemberPermissions: &cmd.DefaultMemberPermissions,
Options: cmd.Options,
Type: cmd.Type,
}
for _, subcommand := range cmd.SubCommands.List() {
applicationCommand.Options = append(applicationCommand.Options, subcommand.ApplicationCommandOption())
}
return applicationCommand
}

func (cmd Command) ApplicationCommandOption() *discord.ApplicationCommandOption {
applicationCommand := cmd.ApplicationCommand()
typ := discord.ApplicationCommandOptionSubCommand

if cmd.SubCommands != nil && cmd.SubCommands.Count() != 0 {
typ = discord.ApplicationCommandOptionSubCommandGroup
}

return &discord.ApplicationCommandOption{
Name: applicationCommand.Name,
Description: applicationCommand.Description,
Options: applicationCommand.Options,
Type: typ,
}
}
126 changes: 126 additions & 0 deletions discord/bot/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package bot

import (
discord "github.com/bwmarrin/discordgo"
)

type OptionsMap = map[string]*discord.ApplicationCommandInteractionDataOption

type Context struct {
*discord.Session
Caller *Command
Interaction *discord.Interaction
Options OptionsMap

handlers []Handler
}

func makeOptionMap(options []*discord.ApplicationCommandInteractionDataOption) (m OptionsMap) {
m = make(OptionsMap, len(options))

for _, option := range options {
m[option.Name] = option
}

return
}

func NewContext(s *discord.Session, caller *Command, i *discord.Interaction, parent *discord.ApplicationCommandInteractionDataOption, handlers []Handler) *Context {
options := i.ApplicationCommandData().Options
if parent != nil {
options = parent.Options
}
return &Context{
Session: s,
Caller: caller,
Interaction: i,
Options: makeOptionMap(options),

handlers: handlers,
}
}

func (ctx *Context) Respond(response *discord.InteractionResponse) error {
return ctx.Session.InteractionRespond(ctx.Interaction, response)
}

func (ctx *Context) Edit(content string) error {
_, err := ctx.Session.InteractionResponseEdit(ctx.Interaction, &discord.WebhookEdit{
Content: &content,
})
return err
}

func (ctx *Context) Response() (*discord.Message, error) {
return ctx.Session.InteractionResponse(ctx.Interaction)
}

func (ctx *Context) Next() {
if ctx.handlers == nil || len(ctx.handlers) == 0 {
return
}

handler := ctx.handlers[0]
ctx.handlers = ctx.handlers[1:]

handler.HandleCommand(ctx)
}

type MessageContext struct {
*discord.Session
Caller *Command
Message *discord.Message

handlers []MessageHandler
}

func NewMessageContext(s *discord.Session, caller *Command, m *discord.Message, handlers []MessageHandler) *MessageContext {
return &MessageContext{
Session: s,
Caller: caller,
Message: m,

handlers: handlers,
}
}

func (ctx *MessageContext) Reply(content string) (m *discord.Message, err error) {
m, err = ctx.Session.ChannelMessageSendReply(
ctx.Message.ChannelID,
content,
ctx.Message.Reference(),
)
return
}

func (ctx *MessageContext) EmbedReply(embed *discord.MessageEmbed) (m *discord.Message, err error) {
m, err = ctx.Session.ChannelMessageSendEmbedReply(
ctx.Message.ChannelID,
embed,
ctx.Message.Reference(),
)
return
}

func (ctx *MessageContext) AddReaction(emojiID string) error {
return ctx.Session.MessageReactionAdd(ctx.Message.ChannelID, ctx.Message.ID, emojiID)
}

func (ctx *MessageContext) RemoveReaction(emojiID string) error {
return ctx.Session.MessageReactionsRemoveEmoji(ctx.Message.ChannelID, ctx.Message.ID, emojiID)
}

func (ctx *MessageContext) ChannelTyping() error {
return ctx.Session.ChannelTyping(ctx.Message.ChannelID)
}

func (ctx *MessageContext) Next() {
if ctx.handlers == nil || len(ctx.handlers) == 0 {
return
}

handler := ctx.handlers[0]
ctx.handlers = ctx.handlers[1:]

handler.HandleMessageCommand(ctx)
}
Loading
Loading