From 015c40441b2e77cf403f8b1b8918c464b7c3dfd8 Mon Sep 17 00:00:00 2001 From: pacnpal <183241239+pacnpal@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:09:27 -0500 Subject: [PATCH] Update README with environment variable configuration and bot command details; enhance event and tool handler initialization --- README.md | 101 +++++++++++++++++- discord_glhf/__pycache__/bot.cpython-313.pyc | Bin 23916 -> 26121 bytes discord_glhf/bot.py | 50 +++++++-- .../__pycache__/event_handler.cpython-313.pyc | Bin 31338 -> 31432 bytes .../__pycache__/tool_handler.cpython-313.pyc | Bin 15664 -> 16622 bytes discord_glhf/handlers/event_handler.py | 9 +- discord_glhf/handlers/tool_handler.py | 10 ++ 7 files changed, 152 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f9f2f12..0f77a79 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,32 @@ uv pip install -r requirements.txt ## Configuration -All configuration is done through environment variables. Create a `.env` file: +### Environment Variables + +Create a `.env` file with required configuration: + +### System Prompt & Personality + +The bot's personality and behavior are configured through `system_prompt.yaml`. This defines: +- Core personality traits and behavior +- Response patterns and style +- Tool usage guidelines +- Context handling rules + +Example system_prompt.yaml structure: +```yaml +sections: + - title: "Personality" + content: "Define the bot's character and tone" + - title: "Tools" + content: "Define how the bot uses available tools" + - title: "Context" + content: "Define how the bot handles conversation context" +``` + +The system prompt works in conjunction with the available tools to create natural, contextual interactions. When using `!reset_memory`, the bot reloads this configuration while preserving user data and preferences. + +### Environment Variables ```env # Required Environment Variables @@ -191,10 +216,76 @@ The bot includes comprehensive monitoring: - System state 3. Performance Metrics - - Response times - - Queue latency - - API latency - - Database performance + - Response times + - Queue latency + - API latency + - Database performance + +4. Tool Usage Monitoring + - Detection of tool invocations + - Tool execution tracking + - Success/failure logging + - Comprehensive logging for: + - User mentions + - Thread creation + - Emoji reactions + - Emoticon conversions + - Tool usage summaries per message + +## Bot Commands + +### Owner Commands +- `!reset_memory` - Reset the bot's memory back to initial system prompt + - Tracks number of resets per user + - Preserves user interaction history and preferences + - Shows reset count in response + +## Bot Tools & Capabilities + +The bot uses a natural language tool system that allows it to perform actions based on conversation context: + +### Available Tools + +1. **User Mentions** (@username) + - Naturally mention users in conversation + - Example: "Hey @john, what do you think?" + - Automatically resolves nicknames and usernames + +2. **Emoji Reactions** + - Add emoji reactions to messages + - Supports Unicode emojis, custom emojis, and standard Discord emojis + - Example: "That's awesome! 👍" (will add thumbs up reaction) + - Can convert emoticons like :) to proper emojis + +3. **Rich Embeds** + - Create formatted embed messages + - Supports titles, descriptions, and custom colors + - Example: + ``` + [Embed] + Poll Results + First place: X + Second place: Y + [/Embed] + ``` + +4. **Threading** + - Automatically creates discussion threads + - Supports various conversation patterns: + * Comparisons: "X vs Y" + * Reviews: "This coaster is overrated" + * Topics: "Safety discussion" or "Maintenance review" + - Automatically formats thread names for consistency + +### Tool Usage + +The bot uses these tools naturally in conversation without requiring explicit commands. It can: +- Recognize when to mention users based on context +- Add appropriate emoji reactions to messages +- Create organized threads for discussions +- Format information in embeds for better readability + +All tools are used through natural language processing, making interactions feel more conversational and intuitive. ## Running the Bot diff --git a/discord_glhf/__pycache__/bot.cpython-313.pyc b/discord_glhf/__pycache__/bot.cpython-313.pyc index c8ceab0b5448a372568f387da69de3ec91b84a09..da38c59727eafa158e182fe9a5641fae7b1cc3c4 100644 GIT binary patch delta 3060 zcmcgtdu&tJ89&F@k89s!J8|sz6=LUQuI)TX^CodhfDk(mXcIgh?wYy8fRVA&Yo|a& z!#AcK#H5R4pF)lDgShus-#4Pl|Qy~t}zLy z-Ts+&5`XtQ-|zc==eysz=R5!S9XxX#YHq95DnQ0_fByZU!|fL}*AzOAOn*#%xBnys zpbXf-IlQaTj2X5;_7?yGGW;xCjCVO%jUPmaM|K2M_(Q%3Kd;n6E&hDXLHUToc_mWY zLBNnR`*CfjA;*St*86OODh(H~=8Rd_i%o_21Z&2Z*#e0l;17HB(2pm!7h<-_pc|02 zC`eY?Tu7EDuM`Z(8(`LG2PA6Cb_2BITlE%fR9Z3KQGlPYS|nL-3-MO0c?ZgKWc>~t z%s1f6E+uvp7vSwp21&n2A{lDN7n;nE@X%G2Ydixg98=Z7+xTr&R{@{nRDhrZfG8wO z-)t_{4{&~{0dN|T;rO615{-tVhZbbCUKG|86!{rXN>%kyX8{(A8LMEGwbdi-#(}37xk9Oo>zKi?4CJ& z`;xO^YADmVBi*=T*4agI#{;}|PT#h~*G+AI&wJ53%X=tV@c`XCr*B=_>ui`e6}`c} z&XPyQ|n+S8uoo!iQGC7{Sx7rQyFVqc5pAEj4 z=dA=kgxh2E2)#&t#neXhkM+>2k$-F`p?V`n{HG!G z=1Hd|QmV@#A)Hn~s;eOJnMR`2h%!&K>*QXiV%h|~Ry190Ms!j^7oO(DV5g{8s(eMv zbYqFn$Xqqbh<>$*@!3+RG%*<*O5G`VRt8U{!sZDjY){=QHA47Cs-RrXLJzi7egJ1u z^OZdeY{%uByztA^(M^Apz^_u(b-z*?JoF2pCr)^Y3h~Qo$182`!MmyR9-pl6hs69c zxG84E4Dq8ryvkRezzCp06dM(;Vte`+*Yr|?`&&<2>*4PUyULx zaS+FDM7n^}fa>9Z+yaiU4fpL)$vQ;w5W=iO*oP3Qh#0W8bL$4m$gM{oFv~3bz zqNZMI>J?@E2rqWNMKUB^iS!Vgycq08{hRQLrU2IAx11IYX_)lg47dT zgA#HgrhLP&GgZ=kQ37|RW_v$?aBpg2=P3z%I%R&+D1qTr?SPd!_W6%M0blapfirmO z2d~L~2Ec9xPmqtH)v3oXSe3HmLzd6^a(U1%n&^F3KzAE&++z~mHJy5L=don>FH1{s zQi)WDnCzcZir?Q;$qEv(r#qc${P#UYmEy51B?jSTD%z=7JEg;PIzz;QG;}1n$zv18CQnYDe3!YPIIp;X&Z7+fc$$Aa!*`_lj(Mcb zpbcrXA%kktsAdk;enA4e&UB6Sp6SgZbI7(tY~Hip@h5-Mv&;eKRycn0bdx~8^ikv1 zSi&BT+vA5v5(#pr*oR`rqQ2_t>QOaC4hX^c;n2`#`)E<8AjAaw(1<|24u+$_i2Xn$ z6pW4x6VyPRDF+R=ABY_tj)W2+rwyC;MUxBpJ)slN#Dc<*FPaDm!pLx9K{aq9o(LW7 zJRVL6^vV;&a&=H`D-{nawV;WHjtz$B4LcZ##fBF&@lay@Tt{xh!I5Yp92v~5D~Q#d zAUsaB5Ww&3+hSfo_mdS#1iCMAH4%3F)jlit4bhru`CpZ!^!w#d#>q^pMc5W_B=xeY zFIa9&aYix5o#Dni=UB^fo>J){b$+DsOV-kiwe}Nh?X0!#bnhZ6$e_|sQ0bd(b7<2% zGG&lEjoj~UyWl2H^ES<`p=Hl83=zQS0&E^x)xQSbABX_v#dnYo%L>8=7-bxNvR zExrckYJ-gGn;B~NsJdOuHHEpmg1J^9qk0wWbGw;qH68W6<53@%gtXkgB04X{> TI?I@EO1YPO%VcU1PsqOkFasfZ delta 1530 zcmZ8gZA?>F7(S=>-rn1uwnAy4v;|uF0fhp}hlqkYW!^XR>5fOq|=2?VKBR>igrl&ppp` z-uvEj-goRL{QD2su%Op#5seiWE)4y#Y}#-`gtbi?GRz_5LJl;6%3<}A65ym3?0`0@ z6%*)?%5g88BW5_s7sJ<@VqFW*QXtfehBa_jQx2ll1XD%{x`_xQS`#jTX@?(f6ggpB z(!r5%8Eh!xA?C0_L0>UQ)}ohgW8bUp4H}~A5g#>{=bAv;3aK?j*Pk zDbj)wdWepqJa2`w21|Q&)6|;t;py=0>W=X>cTAo+U(0=q{j79GI_JJu*?gFbA(WTt{4YAe6ECN+=gkwoEftkmtpi4oIvRbFX4jC@;?Ph0r$$o#rp(&K>o#cT7x^}l&1vaaru;2 zE#Xy=t-pvb%j+7}a<~J|G=}l4{9EHC75-KJs^vS4c@?__H-qC0l>6y`-K);yhjRJq zF2TH=E}uXT_{PV)QE)%0whP95I~)lN6sV~}`x7nPr?`VmKw(`aG;Gy?HP}Y`bE?qs zsb7M_!8$z|mZ(pOO$1UQGyE7dnMpJ5QCrbospYwrP`#mwuc_P{s`Q%5E9#aqmX*A! zyv#1V++PH`aGRbv^~{-&dI?0rU(%vkZF@-(lx@_y9cYMmq9{8YC1|ggB07vw*11YV zs+C@EXeX8mYL2qgQ-WG6=vZi@UP!Dchb!;6RlY*`#=HG0dLuR6DZEtvqI+6}SIg@n z7cuUV$)=Mk+%3cA5*6MifBT+|8Lw~Ki+4d|^r@((6R{Gr6L!MJ?fXl;foSp{B|4+k67b4J%~qvxj1lNXW2eX2FxMl&9=bS6g}nUQ2F6(4jA9CYk? zo_OSto+-Q1vCRAN!Q~EL3Eb^Z*wXW?mjCN}i@kPI@af?4&Q5Kb6`Wx@*kd-5X9}zC z2$nlzYAlF?_lXJC4S0Q`V3?OkRvZ(vWQ>dlewM6%&Jp_p!ouP?$&e+VkejCZyHW!P zyWFn7GUYVY3}*{gpqUCs$j4pB8irbH$jx1M3n8Bz8I&;2Lo{BApTWoRowaZCxb6{R g_&6eM#n_NPphCRv)S=s4=>wh|@5*zCw<_oGAF@V;vj6}9 diff --git a/discord_glhf/bot.py b/discord_glhf/bot.py index 44b62bb..9adbb1d 100644 --- a/discord_glhf/bot.py +++ b/discord_glhf/bot.py @@ -57,11 +57,26 @@ class DiscordBot: try: async with self._init_lock: if not self._initialized: - # Initialize handlers first + # Initialize all handlers first + self.message_handler = MessageHandler(self.db_manager) + logger.info("Message handler initialized") + + self.image_handler = ImageHandler(self.api_manager) + logger.info("Image handler initialized") + self.tool_handler = ToolHandler(self.bot) + logger.info("Tool handler initialized") + self.event_handler = EventHandler( - self.bot, self.queue_manager, self.db_manager, self.api_manager + self.bot, + self.queue_manager, + self.db_manager, + self.api_manager, + message_handler=self.message_handler, + image_handler=self.image_handler, + tool_handler=self.tool_handler ) + logger.info("Event handler initialized with all handlers") # Start API manager if not self.api_manager.is_running: @@ -367,23 +382,40 @@ def run_bot(): """Handle shutdown signals.""" signame = signal.Signals(signum).name logger.info(f"Received signal {signame}") - if bot.bot and bot.bot.loop: - # Use the sync_close method to properly handle shutdown - bot.sync_close() - raise KeyboardInterrupt() # Break the event loop + + # Force stop the event loop + if bot.bot and bot.bot.loop and bot.bot.loop.is_running(): + bot.queue_manager.set_shutting_down() + # Stop the loop immediately + bot.bot.loop.stop() + # This will exit discord.py's run() method + sys.exit(0) # Register signal handlers signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: - # Start the bot + # Start the bot with discord.py's runner bot.bot.run(token) - except KeyboardInterrupt: - logger.info("Bot stopped by interrupt") + except (KeyboardInterrupt, SystemExit): + logger.info("Bot is shutting down...") except Exception as e: logger.error(f"Bot crashed: {e}") raise + finally: + # Final cleanup + try: + # Make sure QueueManager is stopped + bot.queue_manager.set_shutting_down() + # Run cleanup in a new event loop + cleanup_loop = asyncio.new_event_loop() + asyncio.set_event_loop(cleanup_loop) + cleanup_loop.run_until_complete(bot.stop()) + cleanup_loop.close() + except Exception as e: + logger.error(f"Error during final cleanup: {e}") + logger.info("Bot shutdown complete") if __name__ == "__main__": diff --git a/discord_glhf/handlers/__pycache__/event_handler.cpython-313.pyc b/discord_glhf/handlers/__pycache__/event_handler.cpython-313.pyc index 0ae6d3b1e7116e692152a224817952c408e2e3c6..7cab6e0d96b176a61c0c933fb8e59f7470ca90cd 100644 GIT binary patch delta 599 zcmZ9IKWG#|6vpSx?BBcG-gy_3-0d1yuepH#a%>S1gaa{%vI>t=lo5=QQ;Y%KLt~@C zM(oEFXJaQ=q|mW5gh@V+=_CTQ&l6eF|WuF zchE%^Sy;%ZiyqHq#K2-k@>t?S1F|b41uSzSw0WYB|-cp~jiTz@YwyN;2=goHuO>b#d1g(sh$DmV!Z}RBDl))ECCx13z zT5eo?S%F#kZ2m8UW%=~Z8-VxnRCk;~DsJ5SWlpd-j>UzAHD}Ann} zOEDC&2D8Ml1+xM%8xXStF-HuW30E+u6hjtE5qmHfP=q^%BbXzY$Am4I7c9yd%ooE2 z7GVkI2a9kA3&ik1MFhbjyum^-d{7ZZVXzQCnqeXk!{iu(6(+|siSvW3kpNmJ335a* z&*WAnd1;8XvS5|MF(SbV!O}58!7?!d!5owKGf6Y^Xev&=%jC$E%sN?-S%m2n!(?-2 zlgYKr3X|6Z@egaB$#0n-FtScQ$0EgA%nA&Z$^Thu8AB)6vPvu6k=I?3by?oBgXIc` z%pFfa0 zCcyY&^CaC9%#61-M;pG?V07E;8MRWFF=g}T+@H*hYc{_v+``CsX!D|CIcCP=lMj}? lpS-pFCdVfhE>;)D4-%WFRVXv@`*SmDd}08RMan?u0RT@Uc*Ot! diff --git a/discord_glhf/handlers/__pycache__/tool_handler.cpython-313.pyc b/discord_glhf/handlers/__pycache__/tool_handler.cpython-313.pyc index c3128035cfceb7c17c54eef29709aedec47c9779..1a32f494bd65733f4e860536e7d6d829512c3fa9 100644 GIT binary patch delta 1773 zcmaJZb@n&*|~-%K#j~P%D4V>EM?^ z7u*xH5!FckJI)yF4tOEx)NT=^F+mgjHK-(NbnXSIDjfqyKAFxAwt`=D6vMt_=pPK1~XjhYShd>vioXRxC++DQBMX7u@pyfGEUB#wu43Dwf$K$YhkS{ z%{H;NfKo7VrfEAo&DcmR$NriefJE4YU1!F{3q1P9Le-iGQna1y_=m3;1ryh1JliLs>j0QWwnwtXaRFf4F-TZkiawq>zo?;Nte8t zv%>Q|_9mBL9ksRsw(8SF8jQ$YAWe7~c(bSSX5TBh+%m)EdB)F7Cd52H$n53|e6ql& zm@F?8GC6@+NfZjaC@>j;S@&w-;p&(6UEc^?kEo!(;4_Hm=BR7O}} z(itHYJCf%`rXV3E6U)m?Iwu#I4)|m5FQH=pQ#+F)pD6IL!XoymN4llvD?6ei#i6G* zCsL^x?n_n|`7g4_@Y&o_22arTmD1tu1IBW~YrI&HxZ}z~CW)0OVri6KK&^{qI)e-SzK)@a1sGko$hH#M_F<|_`Zh@8 zsj1VdQ$!^p1C;5#JpvJ^drW{x9Au}r?cK{;37xKy|m94iIK5H*H$WB2bU!7Oo{ zxc9djkWFU>1Kczg|Lxt{z3t#DW2a2iyV;(;tNE@jUwm|P<(-4DPk8Q3NCbGhj!z_g?ADQLBRv_od@Th^L>Z;afUoR z*@GCMPpe&oEEaM)SOvu`ab;N8TimF44m1vHXj3?&EoJpkxQMf_IH-ocqq z0c6>RkhB(cFk%%%tZPc!bEu@|6Af@gw&@dYJ z)7>RXQR>im&8=}%O!9xz3d0Cz(xQ^?VKT!gw8|A*%ES%*5w@8+HCf{{p1S^>GQ6!M zj=HIbdZ|nmT9p)ZMN`K7cqtqVGOgK?R^6$~Wim=?yV`<()-?ThO|G;?vzn*I_ibGQ zyBj6g?^3XFQ*)bErNLORB)=Tn`38uv}&c;~~YmVVk$0z*WIgWE(i}+-7%Ihf{ zVp|GRCXATSjHG+3!)b<%3}Sv9>Rz@Lx>!8x$JTTZhtlba^XxY`4MuQ3&ZiHfm}$bD z8P#Tk`W<9d#G{$rxSUB@t09p!!YE}o;m2&k5{erEypZi?N!;p!4H-j0sV9)z%Ym$1 zo?2+cBUh5fBnP4T8~d*AyE(U{c79N^i)waB&7Iq|EUOE1_l|!^_%?zJA1_b6_DE$g^FE{pc%)M$&FnnECIq2bE$J9ZQe`6&q zej^Hl9!%yI$hF5Wa}tTp5TW6-x5^OAz_3D^}YbPk5Buet~m>7SOpV{jlTgc71yNz diff --git a/discord_glhf/handlers/event_handler.py b/discord_glhf/handlers/event_handler.py index 8aefe3c..80d0beb 100644 --- a/discord_glhf/handlers/event_handler.py +++ b/discord_glhf/handlers/event_handler.py @@ -19,14 +19,15 @@ from .tool_handler import ToolHandler class EventHandler: """Handles Discord events and their processing.""" - def __init__(self, bot, queue_manager, db_manager, api_manager): + def __init__(self, bot, queue_manager, db_manager, api_manager, message_handler=None, image_handler=None, tool_handler=None): self.bot = bot self.queue_manager = queue_manager self.db_manager = db_manager self.api_manager = api_manager - self.message_handler = MessageHandler(db_manager) - self.image_handler = ImageHandler(api_manager) - self.tool_handler = ToolHandler(bot) + # Use provided handlers or create new ones + self.message_handler = message_handler if message_handler else MessageHandler(db_manager) + self.image_handler = image_handler if image_handler else ImageHandler(api_manager) + self.tool_handler = tool_handler if tool_handler else ToolHandler(bot) # Set this handler as the queue's message processor self.queue_manager._message_handler = self._process_message diff --git a/discord_glhf/handlers/tool_handler.py b/discord_glhf/handlers/tool_handler.py index 2fc03fb..5eb4501 100644 --- a/discord_glhf/handlers/tool_handler.py +++ b/discord_glhf/handlers/tool_handler.py @@ -257,8 +257,10 @@ class ToolHandler: # Simple pattern matching for basic tool usage # Let the LLM decide most tool usage through natural language if "@" in line: # Basic mention support + logger.info(f"Tool Use - Parse: Detected mention pattern in line: '{line}'") for match in re.finditer(r"@(\w+(?:\s+\w+)*)", line): name = match.group(1).strip() + logger.info(f"Tool Use - Parse: Adding find_user tool call for '{name}'") tool_calls.append(("find_user", {"name": name})) command_found = True @@ -275,6 +277,7 @@ class ToolHandler: match = re.search(pattern, line, re.IGNORECASE) if match: thread_name = re.sub(pattern, name_format, match.group(1)) + logger.info(f"Tool Use - Parse: Adding create_thread tool call for '{thread_name}'") tool_calls.append(("create_thread", { "channel_id": channel_id, "name": thread_name, @@ -290,6 +293,7 @@ class ToolHandler: for match in emoji_matches: emoji = match.group(1) if emoji.strip(): + logger.info(f"Tool Use - Parse: Adding add_reaction tool call for emoji '{emoji}'") tool_calls.append(("add_reaction", { "emoji": emoji, "message_id": message_id, @@ -307,6 +311,7 @@ class ToolHandler: } for pattern, emoji in emoticon_map.items(): if re.search(pattern, line): + logger.info(f"Tool Use - Parse: Converting emoticon to emoji reaction '{emoji}'") tool_calls.append(("add_reaction", { "emoji": emoji, "message_id": message_id, @@ -321,6 +326,11 @@ class ToolHandler: # Join response lines, removing empty lines at start/end final_response = "\n".join(response_lines).strip() + + # Log summary of detected tools + if tool_calls: + logger.info(f"Tool Use - Parse: Detected {len(tool_calls)} tool calls: {[call[0] for call in tool_calls]}") + return tool_calls, final_response, self.mentioned_users except Exception as e: