Added a greet plugin for messages to be sent when people join a channel.

This commit is contained in:
Storm Dragon 2025-03-30 04:10:46 -04:00
parent e9e995167b
commit d97e7fa7b5
7 changed files with 403 additions and 1 deletions

66
Greet/README.md Normal file
View File

@ -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.

9
Greet/__init__.py Normal file
View File

@ -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

Binary file not shown.

27
Greet/config.py Normal file
View File

@ -0,0 +1,27 @@
###
# Copyright (c) 2025, Stormux
#
# This plugin is licensed under the same terms as Limnoria itself.
###
import supybot.conf as conf
import supybot.registry as registry
def configure(advanced):
# This will be called by supybot to configure this module.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Greet', True)
Greet = conf.registerPlugin('Greet')
conf.registerChannelValue(Greet, 'enabled',
registry.Boolean(False, """Determines whether greetings are enabled for a channel."""))
conf.registerChannelValue(Greet, 'message',
registry.String('', """The greeting message to be sent to the channel."""))
conf.registerChannelValue(Greet, 'timeout',
registry.PositiveInteger(3, """The number of seconds to wait after a join
before sending a greeting."""))
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

254
Greet/plugin.py Normal file
View File

@ -0,0 +1,254 @@
###
# Copyright (c) 2025, Stormux
#
# This plugin is licensed under the same terms as Limnoria itself.
###
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import supybot.world as world
import supybot.schedule as schedule
import supybot.conf as conf
import supybot.ircmsgs as ircmsgs
import time
import re
class Greet(callbacks.Plugin):
"""
A plugin to greet users when they join a channel.
Features flood protection and timeout between greetings.
"""
def __init__(self, irc):
self.__parent = super(Greet, self)
self.__parent.__init__(irc)
# Dictionary to store last greeting times per user
self.lastGreetTime = {}
# Default cooldown between greetings for the same user (in seconds)
self.userCooldown = 300 # 5 minutes
# Debug mode
self.debugMode = False
def doJoin(self, irc, msg):
"""
Called when someone joins a channel.
"""
channel = msg.args[0]
nick = msg.nick
# Log event for debugging
if self.debugMode:
self._logDebug(irc, f"JOIN event detected: {nick} joined {channel}")
# Don't greet ourselves
if nick == irc.nick:
if self.debugMode:
self._logDebug(irc, f"Not greeting self (bot)")
return
# Check if greetings are enabled for this channel
if not self.registryValue('enabled', channel):
if self.debugMode:
self._logDebug(irc, f"Greetings disabled for {channel}")
return
# Get the greeting message for this channel
greeting = self.registryValue('message', channel)
if not greeting:
if self.debugMode:
self._logDebug(irc, f"No greeting message set for {channel}")
return
# Check if this user was greeted recently
userKey = f"{nick.lower()}@{channel}"
now = time.time()
if userKey in self.lastGreetTime:
lastTime = self.lastGreetTime[userKey]
if now - lastTime < self.userCooldown:
if self.debugMode:
self._logDebug(irc, f"User {nick} was greeted too recently")
return
# Get the timeout
timeout = self.registryValue('timeout', channel) or 3
if self.debugMode:
self._logDebug(irc, f"Will greet in {channel} after {timeout}s: {greeting}")
# Update last greet time for this user
self.lastGreetTime[userKey] = now
# Use a closure to capture the current values
def sendGreeting(channelToGreet=channel, greetingMsg=greeting):
if self.debugMode:
self._logDebug(irc, f"Sending greeting in {channelToGreet}: {greetingMsg}")
try:
# Make sure the channel is still valid when we send the message
if channelToGreet in irc.state.channels:
irc.queueMsg(ircmsgs.privmsg(channelToGreet, greetingMsg))
except Exception as e:
if self.debugMode:
self._logDebug(irc, f"Error sending greeting: {str(e)}")
# Schedule the greeting
schedule.addEvent(sendGreeting, time.time() + timeout)
def _logDebug(self, irc, message):
"""Send debug messages to the first available channel"""
try:
# Send to the owner via private message instead of to a channel
for owner in self.registryValue('owners') or []:
irc.queueMsg(ircmsgs.privmsg(owner, f"[GREET-DEBUG] {message}"))
return
# Fallback: send to the first available channel
for channel in list(irc.state.channels):
try:
irc.queueMsg(ircmsgs.privmsg(channel, f"[GREET-DEBUG] {message}"))
break
except:
continue
except Exception as e:
# Last resort, log to Supybot's logs
self.log.error(f"Debug logging error: {str(e)}")
@wrap(['channel'])
def greettest(self, irc, msg, args, channel):
"""[<channel>]
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):
"""[<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):
"""<on|off>
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):
"""[<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):
"""[<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):
"""[<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):
"""[<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):
"""[<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):
"""[<channel>] <message>
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 <seconds> - 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:

46
Greet/test.py Normal file
View File

@ -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:

View File

@ -1,5 +1,5 @@
###
# Copyright (c) 2025, Your Name
# Copyright (c) 2025, Stormux
#
# This plugin is licensed under the same terms as Limnoria itself.
###