From d97e7fa7b5648b384885279ea770930823add78b Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 30 Mar 2025 04:10:46 -0400 Subject: [PATCH] Added a greet plugin for messages to be sent when people join a channel. --- Greet/README.md | 66 ++++++ Greet/__init__.py | 9 + Greet/__pycache__/plugin.cpython-313.pyc | Bin 0 -> 11580 bytes Greet/config.py | 27 +++ Greet/plugin.py | 254 +++++++++++++++++++++++ Greet/test.py | 46 ++++ Weather/plugin.py | 2 +- 7 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 Greet/README.md create mode 100644 Greet/__init__.py create mode 100644 Greet/__pycache__/plugin.cpython-313.pyc create mode 100644 Greet/config.py create mode 100644 Greet/plugin.py create mode 100644 Greet/test.py diff --git a/Greet/README.md b/Greet/README.md new file mode 100644 index 0000000..d65775a --- /dev/null +++ b/Greet/README.md @@ -0,0 +1,66 @@ +# Greet Plugin for Limnoria + +A simple plugin that greets users when they join a channel, with built-in flood protection and timeout features. + +## Features + +- Set custom greeting messages per channel +- Timeout between join and greeting to handle multiple users joining at once +- Cooldown feature to prevent flooding for users with unstable connections +- Simple commands to enable, disable, or clear greetings +- Debug mode for easier troubleshooting + +## Installation + +1. Copy the `Greet` directory to your Limnoria's plugins directory: + ``` + ~/yourbot/plugins/ + ``` + +2. Load the plugin: + ``` + @load Greet + ``` + +## Commands + +The plugin now uses namespaced commands to avoid conflicts with other plugins: + +- `greet #channel Your greeting message` - Sets the greeting message and enables it +- `greetstatus #channel` - Shows the current greeting status and message +- `greeton #channel` - Enables the greeting for a channel +- `greetoff #channel` - Disables the greeting for a channel +- `greetclear #channel` - Clears the greeting message and disables it +- `greettimeout #channel 5` - Sets the timeout in seconds (default is 3) +- `greettest #channel` - Tests the greeting by sending it immediately +- `greettrigger #channel` - Simulates a join event with timeout (owner only) +- `greetdebug on|off` - Enables or disables debug mode (owner only) + +## Examples + +``` +.greet #a11y-offtopic Welcome to the off-topic channel! +.greetstatus #a11y-offtopic +.greettimeout #a11y-offtopic 5 +.greetoff #a11y-offtopic +.greeton #a11y-offtopic +.greetclear #a11y-offtopic +.greettest #a11y-offtopic +``` + +## How It Works + +When a user joins a channel, the plugin checks if greetings are enabled for that channel. If enabled, it waits for the configured timeout period to see if more users join. After the timeout expires, it sends the greeting message. + +The plugin also implements a cooldown period (5 minutes by default) for each user to prevent multiple greetings to the same user in case of connection issues. + +## Troubleshooting + +If you're experiencing issues: + +1. Enable debug mode: `.greetdebug on` (owner only) +2. Test the greeting: `.greettest #channel` +3. Check if the greeting is enabled: `.greetstatus #channel` +4. Try triggering a simulated join: `.greettrigger #channel` (owner only) + +The debug mode will output information about join events and greeting attempts to help diagnose any problems. diff --git a/Greet/__init__.py b/Greet/__init__.py new file mode 100644 index 0000000..82527db --- /dev/null +++ b/Greet/__init__.py @@ -0,0 +1,9 @@ +### +# Copyright (c) 2025, Stormux +# +# This plugin is licensed under the same terms as Limnoria itself. +### + +from . import config, plugin + +Class = plugin.Class diff --git a/Greet/__pycache__/plugin.cpython-313.pyc b/Greet/__pycache__/plugin.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69b913b5f4e8f62268efdeb270ae75af51c67da1 GIT binary patch literal 11580 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] + + Test the greeting by sending it immediately. + """ + greeting = self.registryValue('message', channel) + if greeting: + irc.queueMsg(ircmsgs.privmsg(channel, greeting)) + irc.replySuccess("Greeting test sent") + else: + irc.error("No greeting message is set for this channel") + + @wrap(['owner', 'channel']) + def greettrigger(self, irc, msg, args, channel): + """[] + + Simulates a join event with timeout (owner only). + """ + greeting = self.registryValue('message', channel) + if not greeting: + irc.error(f"No greeting message is set for {channel}") + return + + # Get the timeout + timeout = self.registryValue('timeout', channel) or 3 + + irc.replySuccess(f"Simulating join event with {timeout}s delay") + + # Schedule the greeting + def sendGreeting(): + irc.queueMsg(ircmsgs.privmsg(channel, greeting)) + + schedule.addEvent(sendGreeting, time.time() + timeout) + + @wrap(['owner', 'boolean']) + def greetdebug(self, irc, msg, args, enable): + """ + + Enables or disables debug mode (owner only). + """ + self.debugMode = enable + if enable: + irc.replySuccess("Debug mode enabled") + else: + irc.replySuccess("Debug mode disabled") + + @wrap(['channel']) + def greeton(self, irc, msg, args, channel): + """[] + + Enables greetings for a channel. + """ + self.setRegistryValue('enabled', True, channel) + greeting = self.registryValue('message', channel) + if greeting: + irc.replySuccess(f"Greetings enabled for {channel}. Current greeting: \"{greeting}\"") + else: + irc.error(f"Greetings enabled for {channel}, but no greeting message is set.") + + @wrap(['channel']) + def greetoff(self, irc, msg, args, channel): + """[] + + Disables greetings for a channel. + """ + self.setRegistryValue('enabled', False, channel) + irc.replySuccess(f"Greetings disabled for {channel}.") + + @wrap(['channel']) + def greetclear(self, irc, msg, args, channel): + """[] + + Removes the greeting message for a channel. + """ + self.setRegistryValue('message', '', channel) + self.setRegistryValue('enabled', False, channel) + irc.replySuccess(f"Greeting message cleared and disabled for {channel}.") + + @wrap(['channel']) + def greetstatus(self, irc, msg, args, channel): + """[] + + Shows the current greeting status and message for a channel. + """ + enabled = self.registryValue('enabled', channel) + message = self.registryValue('message', channel) + timeout = self.registryValue('timeout', channel) or 3 + + if enabled and message: + irc.reply(f"Greetings are enabled for {channel}. Timeout: {timeout} seconds. Message: \"{message}\"") + elif enabled and not message: + irc.reply(f"Greetings are enabled for {channel}, but no message is set.") + else: + irc.reply(f"Greetings are disabled for {channel}. Message: \"{message or 'None'}\"") + + @wrap(['channel', 'positiveInt']) + def greettimeout(self, irc, msg, args, channel, seconds): + """[] + + Sets the timeout in seconds between joins before sending a greeting. + Default is 3 seconds. + """ + self.setRegistryValue('timeout', seconds, channel) + irc.replySuccess(f"Greeting timeout for {channel} set to {seconds} seconds.") + + @wrap(['channel', 'text']) + def greet(self, irc, msg, args, channel, text): + """[] + + Sets the greeting message for a channel. + The greeting will be sent when users join the channel, with flood +protection. + + To manage greetings, use the other commands: + greeton - Enables greetings for the channel + greetoff - Disables greetings for the channel + greetclear - Removes the greeting message for the channel + greetdebug on|off - Enables/disables debug mode (owner only) + greettest - Immediately sends the greeting to test the message + greettrigger - Simulates a join event with timeout (owner only) + greetstatus - Shows current status and message + greettimeout - Sets delay before greeting + + Examples: + greet #channel Welcome to our channel! + """ + self.setRegistryValue('message', text, channel) + self.setRegistryValue('enabled', True, channel) + irc.replySuccess(f"Greeting message for {channel} set to: \"{text}\"") + +Class = Greet + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/Greet/test.py b/Greet/test.py new file mode 100644 index 0000000..aef13f4 --- /dev/null +++ b/Greet/test.py @@ -0,0 +1,46 @@ +### +# Copyright (c) 2025, Stormux +# +# This plugin is licensed under the same terms as Limnoria itself. +### + +from supybot.test import * + +class GreetTestCase(PluginTestCase): + plugins = ('Greet',) + + def testGreet(self): + # Set a greeting message + self.assertNotError('greet #test Hello, world!') + # Check if it's enabled + self.assertRegexp('greetstatus #test', 'enabled') + # Disable it + self.assertNotError('greetoff #test') + # Check if it's disabled + self.assertRegexp('greetstatus #test', 'disabled') + # Enable it again + self.assertNotError('greeton #test') + # Check if it's enabled + self.assertRegexp('greetstatus #test', 'enabled') + # Clear it + self.assertNotError('greetclear #test') + # Check if it's cleared and disabled + self.assertRegexp('greetstatus #test', 'disabled') + + def testTimeout(self): + # Set a timeout + self.assertNotError('greettimeout #test 5') + # Check if it's set + self.assertRegexp('greetstatus #test', '5 seconds') + + def testTestAndTrigger(self): + # Set a greeting message + self.assertNotError('greet #test Hello, world!') + # Test it + self.assertNotError('greettest #test') + # Since we can't really test greettrigger in a unit test (it's + # time-based) + # we'll just check if the command is recognized + # Owner-only commands can't be tested directly here + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/Weather/plugin.py b/Weather/plugin.py index 1561320..2ac5a98 100644 --- a/Weather/plugin.py +++ b/Weather/plugin.py @@ -1,5 +1,5 @@ ### -# Copyright (c) 2025, Your Name +# Copyright (c) 2025, Stormux # # This plugin is licensed under the same terms as Limnoria itself. ###