2025-03-31 20:51:39 -04:00

315 lines
12 KiB
Python

###
# 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.
Only greets users the first time they join during a session,
and resets when the greeting message changes.
"""
def __init__(self, irc):
self.__parent = super(Greet, self)
self.__parent.__init__(irc)
# Dictionary to track which users have been greeted in this session
self.greetedUsers = {}
# Dictionary to store the greeting message used for each user
self.userGreetingVersion = {}
# 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 we've already greeted this user with this greeting
userKey = f"{nick.lower()}@{channel}"
if userKey in self.greetedUsers and channel in self.greetedUsers[userKey]:
# If the greeting has changed since we last greeted this user, we should greet them again
lastGreetingUsed = self.userGreetingVersion.get(userKey, "")
if greeting == lastGreetingUsed:
if self.debugMode:
self._logDebug(irc, f"User {nick} already greeted in this session with current greeting")
return
else:
if self.debugMode:
self._logDebug(irc, f"Greeting for {channel} has changed, will greet {nick} again")
# 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}")
# Mark this user as greeted in this channel
if userKey not in self.greetedUsers:
self.greetedUsers[userKey] = set()
self.greetedUsers[userKey].add(channel)
# Store the greeting version we're using for this user
self.userGreetingVersion[userKey] = greeting
# 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 resetgreethistory(self, irc, msg, args, channel):
"""[<channel>]
Resets the greeting history for the channel, causing all users to be
greeted again
on their next join (even if they were greeted before).
"""
# Clear the greeting history for this channel
for userKey in list(self.greetedUsers.keys()):
if channel in self.greetedUsers[userKey]:
self.greetedUsers[userKey].remove(channel)
# If this user has no more channels, remove the entry
# completely
if not self.greetedUsers[userKey]:
del self.greetedUsers[userKey]
if userKey in self.userGreetingVersion:
del self.userGreetingVersion[userKey]
irc.replySuccess(f"Greeting history for {channel} has been reset.")
@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
# Count how many users have been greeted in this channel
greetedCount = sum(1 for userKey in self.greetedUsers if channel in
self.greetedUsers[userKey])
if enabled and message:
irc.reply(f"Greetings are enabled for {channel}. Timeout: {timeout} seconds. Message: \"{message}\"")
irc.reply(f"Users greeted in this session: {greetedCount}")
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 for the first
time
in a session. If the greeting message changes, users will be greeted
again.
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
resetgreethistory - Resets who has been greeted, greeting everyone
again
Examples:
greet #channel Welcome to our channel!
"""
oldGreeting = self.registryValue('message', channel)
self.setRegistryValue('message', text, channel)
self.setRegistryValue('enabled', True, channel)
# If greeting message has changed, reset who has been greeted with this
# message
if oldGreeting != text:
# Mark that the greeting has changed by removing this channel from
# all users' greeted lists
for userKey in list(self.greetedUsers.keys()):
if channel in self.greetedUsers[userKey]:
self.greetedUsers[userKey].remove(channel)
# If this user has no more channels, remove the entry
# completely
if not self.greetedUsers[userKey]:
del self.greetedUsers[userKey]
if userKey in self.userGreetingVersion:
del self.userGreetingVersion[userKey]
irc.replySuccess(f"Greeting message for {channel} set to: \"{text}\" (greeting history reset)")
else:
irc.replySuccess(f"Greeting message for {channel} set to: \"{text}\"")
Class = Greet
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: