Skip to content

Commit

Permalink
Merge pull request #3 from jgknight/expression-enhancements
Browse files Browse the repository at this point in the history
Fix crash on Talk/DC requests, add profile and message format support
  • Loading branch information
mk6i authored Jun 22, 2024
2 parents a16ec14 + 9e57577 commit 8027f08
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 8 deletions.
51 changes: 43 additions & 8 deletions client/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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{}
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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())
}
}
Expand All @@ -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
}
Expand All @@ -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,
Expand All @@ -393,7 +404,8 @@ func sendMessageSNAC(msgCh chan<- wire.SNACMessage, cookie uint64, screenName st
ScreenName: screenName,
}

response = fmt.Sprintf(`<HTML><BODY BGCOLOR="#ffffff">%s</BODY></HTML>`, 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)
}
Expand Down Expand Up @@ -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
}
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:"'<HTML><BODY BGCOLOR=\"#CDFFFE\"><FONT FACE=\"Courier New\" COLOR=\"#000080\" LANG=\"0\">Hello, %n!<BR>Send me an IM to get started!</FONT><BR><BR><HR><FONT SIZE=1>Powered by <A HREF=\"https://github.com/mk6i/smarter-smarter-child\">SmarterSmarterChild</A>.</FONT></BODY></HTML>'" description:"The bot's HTML profile information."`
MsgFormat string `envconfig:"MSG_FORMAT" required:"true" val:"'<HTML><BODY BGCOLOR=\"#CDFFFE\"><FONT FACE=\"Courier New\" COLOR=\"#000080\" LANG=\"0\">@MsgContent@</FONT></BODY></HTML>'" description:"The bot's message response. @MsgContent@ will be replaced with the content of the bot's response."`
}
2 changes: 2 additions & 0 deletions config/ras.service
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Environment="PASSWORD="
Environment="SCREEN_NAME=smartersmarterchild"
Environment="WORD_COUNT_LIMIT=25"
Environment="WORD_LENGTH_LIMIT=15"
Environment=PROFILE_HTML='<HTML><BODY BGCOLOR="#CDFFFE"><FONT FACE="Courier New" COLOR="#000080" LANG="0">Hello, %n!<BR>Send me an IM to get started!</FONT><BR><BR><HR><FONT SIZE=1>Powered by <A HREF="https://github.com/mk6i/smarter-smarter-child">SmarterSmarterChild</A>.</FONT></BODY></HTML>'
Environment=MSG_FORMAT='<HTML><BODY BGCOLOR="#CDFFFE"><FONT FACE="Courier New" COLOR="#000080" LANG="0">@MsgContent@</FONT></BODY></HTML>'
ExecStart=/opt/ssc/smarter_smarter_child
Restart=on-failure

Expand Down
7 changes: 7 additions & 0 deletions config/settings.bat
Original file line number Diff line number Diff line change
Expand Up @@ -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='<HTML><BODY BGCOLOR="#CDFFFE"><FONT FACE="Courier New" COLOR="#000080" LANG="0">Hello, %n!<BR>Send me an IM to get started!</FONT><BR><BR><HR><FONT SIZE=1>Powered by <A HREF="https://github.com/mk6i/smarter-smarter-child">SmarterSmarterChild</A>.</FONT></BODY></HTML>'

rem The bot's message response. @MsgContent@ will be replaced with the content
rem of the bot's response.
set MSG_FORMAT='<HTML><BODY BGCOLOR="#CDFFFE"><FONT FACE="Courier New" COLOR="#000080" LANG="0">@MsgContent@</FONT></BODY></HTML>'

7 changes: 7 additions & 0 deletions config/settings.env
Original file line number Diff line number Diff line change
Expand Up @@ -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='<HTML><BODY BGCOLOR="#CDFFFE"><FONT FACE="Courier New" COLOR="#000080" LANG="0">Hello, %n!<BR>Send me an IM to get started!</FONT><BR><BR><HR><FONT SIZE=1>Powered by <A HREF="https://github.com/mk6i/smarter-smarter-child">SmarterSmarterChild</A>.</FONT></BODY></HTML>'

# The bot's message response. @MsgContent@ will be replaced with the content of
# the bot's response.
export MSG_FORMAT='<HTML><BODY BGCOLOR="#CDFFFE"><FONT FACE="Courier New" COLOR="#000080" LANG="0">@MsgContent@</FONT></BODY></HTML>'

0 comments on commit 8027f08

Please sign in to comment.