diff --git a/client/chat.go b/client/chat.go index fe6e8ce..b70b097 100644 --- a/client/chat.go +++ b/client/chat.go @@ -80,6 +80,10 @@ func Chat(logger *slog.Logger, flapc FlapClient, authCookie string, chatBot Chat return err } + if err := sendInfoSNAC(flapc, config); err != nil { + return err + } + msgCh := make(chan wire.SNACMessage, 10) // send client->server messages @@ -127,10 +131,11 @@ func Chat(logger *slog.Logger, flapc FlapClient, authCookie string, chatBot Chat } case snacFrame.FoodGroup == wire.OService && snacFrame.SubGroup == wire.OServiceEvilNotification: // received a warning, let's respond - if err := reactToWarning(logger, msgCh, chatContexts, flapBody, chatBot); err != nil { + if err := reactToWarning(logger, msgCh, chatContexts, flapBody, chatBot, config); err != nil { return err } } + } return nil @@ -172,6 +177,7 @@ func reactToWarning( chatContexts map[string]*chatContext, flapBody *bytes.Buffer, chatBot ChatBot, + config config.Config, ) error { chatMsg := wire.SNAC_0x01_0x10_OServiceEvilNotification{} @@ -207,7 +213,7 @@ func reactToWarning( return fmt.Errorf("unable to get response from bot: %w", err) } - if err := sendMessageSNAC(msgCh, chatCtx.cookie, chatMsg.ScreenName, botResponse); err != nil { + if err := sendMessageSNAC(msgCh, chatCtx.cookie, chatMsg.ScreenName, botResponse, config); err != nil { return fmt.Errorf("unable to send response: %w", err) } @@ -272,12 +278,16 @@ func exchangeMessages( // reach the rate limit threshold, inform the user that they are sending // messages too quickly and ignore subsequent messages until the rate limit // window passes. - if hitRateLimit := enforceRateLimit(logger, msgCh, chatCtx, msgSNAC); hitRateLimit { + if hitRateLimit := enforceRateLimit(logger, msgCh, chatCtx, msgSNAC, config); hitRateLimit { logger.Info("user hit message rate limit", "screen_name", msgSNAC.ScreenName) return nil } // Get the message text buried in the SNAC payload. + if _, hasIMData := msgSNAC.TLVRestBlock.Slice(wire.ICBMTLVAOLIMData); !hasIMData { + logger.Debug("received ICBMChannelMsgToClient with no AOLIMData") + return nil + } msgText, err := msgSNAC.ExtractMessageText() if err != nil { return err @@ -312,7 +322,7 @@ func exchangeMessages( } // Send the bot's response. - if err := sendMessageSNAC(msgCh, msgSNAC.Cookie, msgSNAC.ScreenName, botResponse); err != nil { + if err := sendMessageSNAC(msgCh, msgSNAC.Cookie, msgSNAC.ScreenName, botResponse, config); err != nil { logger.Error("unable to send response", "err", err.Error()) return } @@ -338,7 +348,7 @@ func enforceMsgSizeLimit( tooLong := exceedsMsgSizeLimit(text, config) if tooLong { botResponse := "Your message is too long for me! I am but a simple bot!" - if err := sendMessageSNAC(msgCh, msgSNAC.Cookie, msgSNAC.ScreenName, botResponse); err != nil { + if err := sendMessageSNAC(msgCh, msgSNAC.Cookie, msgSNAC.ScreenName, botResponse, config); err != nil { logger.Error("unable to send size limit warning", "err", err.Error()) } } @@ -363,13 +373,14 @@ func enforceRateLimit( msgCh chan wire.SNACMessage, chatCtx *chatContext, msgSNAC wire.SNAC_0x04_0x07_ICBMChannelMsgToClient, + config config.Config, ) bool { if !chatCtx.limiter.Allow() { if !chatCtx.rateLimited { chatCtx.rateLimited = true go func() { botResponse := "You're sending me too many messages! Slow down!" - if err := sendMessageSNAC(msgCh, msgSNAC.Cookie, msgSNAC.ScreenName, botResponse); err != nil { + if err := sendMessageSNAC(msgCh, msgSNAC.Cookie, msgSNAC.ScreenName, botResponse, config); err != nil { logger.Error("unable to send rate limit limit warning", "err", err.Error()) return } @@ -382,7 +393,7 @@ func enforceRateLimit( return false } -func sendMessageSNAC(msgCh chan<- wire.SNACMessage, cookie uint64, screenName string, response string) error { +func sendMessageSNAC(msgCh chan<- wire.SNACMessage, cookie uint64, screenName string, response string, config config.Config) error { msgFrame := wire.SNACFrame{ FoodGroup: wire.ICBM, SubGroup: wire.ICBMChannelMsgToHost, @@ -393,7 +404,8 @@ func sendMessageSNAC(msgCh chan<- wire.SNACMessage, cookie uint64, screenName st ScreenName: screenName, } - response = fmt.Sprintf(`%s`, response) + // build the response message + response = strings.ReplaceAll(config.MsgFormat, "@MsgContent@", response) if err := responseSNAC.ComposeMessage(response); err != nil { return fmt.Errorf("unable to compose message: %w", err) } @@ -438,3 +450,26 @@ var stripHTMLRegex = regexp.MustCompile("<[^>]*>") func stripHTMLTags(input string) string { return stripHTMLRegex.ReplaceAllString(input, "") } + +// Set the bot's profile +func sendInfoSNAC(flapc FlapClient, config config.Config) error { + profileSNAC := wire.SNACMessage{ + Frame: wire.SNACFrame{ + FoodGroup: wire.Locate, + SubGroup: wire.LocateSetInfo, + }, + Body: wire.SNAC_0x02_0x04_LocateSetInfo{ + TLVRestBlock: wire.TLVRestBlock{ + TLVList: wire.TLVList{ + wire.NewTLV(wire.LocateTLVTagsInfoSigMime, `text/aolrtf; charset="us-ascii"`), + wire.NewTLV(wire.LocateTLVTagsInfoSigData, config.ProfileHTML), + }, + }, + }, + } + err := flapc.SendSNAC(profileSNAC.Frame, profileSNAC.Body) + if err != nil { + return err + } + return nil +} diff --git a/config/config.go b/config/config.go index de79165..e91bdf3 100644 --- a/config/config.go +++ b/config/config.go @@ -13,4 +13,6 @@ type Config struct { ScreenName string `envconfig:"SCREEN_NAME" required:"true" val:"smartersmarterchild" description:"The bot's screen name."` WordCountLimit int `envconfig:"WORD_COUNT_LIMIT" required:"true" val:"25" description:"The maximum number of words sent to the bot in a single message."` WordLengthLimit int `envconfig:"WORD_LENGTH_LIMIT" required:"true" val:"15" description:"The maximum length of any word sent to the bot in a single message."` + ProfileHTML string `envconfig:"PROFILE_HTML" required:"true" val:"'Hello, %n!
Send me an IM to get started!



Powered by SmarterSmarterChild.'" description:"The bot's HTML profile information."` + MsgFormat string `envconfig:"MSG_FORMAT" required:"true" val:"'@MsgContent@'" description:"The bot's message response. @MsgContent@ will be replaced with the content of the bot's response."` } diff --git a/config/ras.service b/config/ras.service index 7ab05c9..d93e363 100644 --- a/config/ras.service +++ b/config/ras.service @@ -15,6 +15,8 @@ Environment="PASSWORD=" Environment="SCREEN_NAME=smartersmarterchild" Environment="WORD_COUNT_LIMIT=25" Environment="WORD_LENGTH_LIMIT=15" +Environment=PROFILE_HTML='Hello, %n!
Send me an IM to get started!



Powered by SmarterSmarterChild.' +Environment=MSG_FORMAT='@MsgContent@' ExecStart=/opt/ssc/smarter_smarter_child Restart=on-failure diff --git a/config/settings.bat b/config/settings.bat index acf5eb8..1528f2a 100644 --- a/config/settings.bat +++ b/config/settings.bat @@ -30,3 +30,10 @@ set WORD_COUNT_LIMIT=25 rem The maximum length of any word sent to the bot in a single message. set WORD_LENGTH_LIMIT=15 +rem The bot's HTML profile information. +set PROFILE_HTML='Hello, %n!
Send me an IM to get started!



Powered by SmarterSmarterChild.' + +rem The bot's message response. @MsgContent@ will be replaced with the content +rem of the bot's response. +set MSG_FORMAT='@MsgContent@' + diff --git a/config/settings.env b/config/settings.env index 44b5edc..60c17cf 100644 --- a/config/settings.env +++ b/config/settings.env @@ -30,3 +30,10 @@ export WORD_COUNT_LIMIT=25 # The maximum length of any word sent to the bot in a single message. export WORD_LENGTH_LIMIT=15 +# The bot's HTML profile information. +export PROFILE_HTML='Hello, %n!
Send me an IM to get started!



Powered by SmarterSmarterChild.' + +# The bot's message response. @MsgContent@ will be replaced with the content of +# the bot's response. +export MSG_FORMAT='@MsgContent@' +