170 lines
7.1 KiB
Python
170 lines
7.1 KiB
Python
![]() |
import asyncio
|
||
|
import irc3
|
||
|
import random
|
||
|
from irc3.plugins.command import command
|
||
|
import textwrap
|
||
|
|
||
|
@irc3.plugin
|
||
|
class PeriodicMessagePlugin:
|
||
|
def __init__(self, bot):
|
||
|
self.bot = bot
|
||
|
self.channel = ""
|
||
|
self.periodic_message = ' \u00A0 \u2002 \u2003 ' * 500
|
||
|
self.tasks = []
|
||
|
self.running = False
|
||
|
# Define sleep durations for each task
|
||
|
self.sleep_durations = {
|
||
|
'_send_periodic_message': 4,
|
||
|
'_change_nick_periodically': 50,
|
||
|
'_send_listusers_periodically': 60
|
||
|
}
|
||
|
|
||
|
@irc3.event(irc3.rfc.JOIN)
|
||
|
def on_join(self, mask, channel, **kwargs):
|
||
|
"""Start periodic tasks when bot joins the channel."""
|
||
|
self.channel = channel
|
||
|
if not self.running:
|
||
|
pass
|
||
|
# self.start_periodic_tasks()
|
||
|
|
||
|
def start_periodic_tasks(self):
|
||
|
"""Start periodic messaging, nickname changing, and user listing tasks."""
|
||
|
self.running = True
|
||
|
self._cancel_tasks()
|
||
|
self.tasks = [
|
||
|
asyncio.create_task(self._send_periodic_message()),
|
||
|
asyncio.create_task(self._change_nick_periodically()),
|
||
|
asyncio.create_task(self._send_listusers_periodically())
|
||
|
]
|
||
|
for task in self.tasks:
|
||
|
task.add_done_callback(self._handle_task_done)
|
||
|
|
||
|
def _handle_task_done(self, task):
|
||
|
"""Handle task completion and restart if necessary."""
|
||
|
try:
|
||
|
task.result()
|
||
|
except asyncio.CancelledError:
|
||
|
self.bot.log.info("Task cancelled as expected.")
|
||
|
except Exception as e:
|
||
|
self.bot.log.error(f"Task error: {e}")
|
||
|
finally:
|
||
|
if self.running:
|
||
|
self.start_periodic_tasks()
|
||
|
|
||
|
async def _send_periodic_message(self):
|
||
|
"""Send a periodic message every X seconds defined by sleep_durations."""
|
||
|
try:
|
||
|
while self.running:
|
||
|
self.bot.privmsg(self.channel, self.periodic_message)
|
||
|
self.bot.log.info(f"Message sent to {self.channel}: {self.periodic_message}")
|
||
|
await asyncio.sleep(self.sleep_durations['_send_periodic_message'])
|
||
|
except asyncio.CancelledError:
|
||
|
pass
|
||
|
except Exception as e:
|
||
|
self.bot.log.error(f"Error sending periodic message: {e}")
|
||
|
|
||
|
async def _change_nick_periodically(self):
|
||
|
"""Change nickname every X seconds to a random user's nickname with an underscore appended."""
|
||
|
try:
|
||
|
self.original_nick = self.bot.nick
|
||
|
while self.running:
|
||
|
channel_key = self.channel.lower()
|
||
|
if channel_key in self.bot.channels:
|
||
|
users = list(self.bot.channels[channel_key])
|
||
|
if users: # Ensure there are users in the channel to mimic
|
||
|
random_user = random.choice(users)
|
||
|
new_nick = f"{random_user}_"
|
||
|
self.bot.send(f'NICK {new_nick}')
|
||
|
self.bot.log.info(f"Nickname changed to mimic: {random_user} as {new_nick}")
|
||
|
if new_nick:
|
||
|
self.bot.nick = new_nick
|
||
|
else:
|
||
|
self.bot.log.info("No users in channel to change nick to.")
|
||
|
else:
|
||
|
self.bot.log.info(f"Channel {self.channel} not found for nick change.")
|
||
|
await asyncio.sleep(self.sleep_durations['_change_nick_periodically'])
|
||
|
except asyncio.CancelledError:
|
||
|
pass
|
||
|
except Exception as e:
|
||
|
self.bot.log.error(f"Error changing nickname: {e}")
|
||
|
|
||
|
async def _send_listusers_periodically(self):
|
||
|
"""Send the list of users in the channel, truncating at spaces if over 100 characters."""
|
||
|
try:
|
||
|
while self.running:
|
||
|
channel_key = self.channel.lower()
|
||
|
if channel_key in self.bot.channels:
|
||
|
users = list(self.bot.channels[channel_key])
|
||
|
users_msg = ' '.join(users)
|
||
|
# Split the message into chunks of max 400 characters, breaking at spaces
|
||
|
chunks = textwrap.wrap(users_msg, width=400, break_long_words=False)
|
||
|
for chunk in chunks:
|
||
|
self.bot.privmsg(self.channel, chunk)
|
||
|
await asyncio.sleep(0.0001) # Small delay between chunks
|
||
|
self.bot.log.info(f"User list sent to {self.channel}.")
|
||
|
else:
|
||
|
self.bot.log.info(f"Channel {self.channel} not found.")
|
||
|
await asyncio.sleep(self.sleep_durations['_send_listusers_periodically'])
|
||
|
except asyncio.CancelledError:
|
||
|
pass
|
||
|
except Exception as e:
|
||
|
self.bot.log.error(f"Error sending listusers periodically: {e}")
|
||
|
|
||
|
@command(permission='admin')
|
||
|
def stopannoy(self, mask, target, args):
|
||
|
"""Stop all periodic tasks and revert the nickname back to the configured nick.
|
||
|
|
||
|
%%stopannoy
|
||
|
"""
|
||
|
if mask.nick == self.bot.config.get('owner', ''):
|
||
|
self.running = False
|
||
|
self._cancel_tasks()
|
||
|
# Change nick back to the original configured nick
|
||
|
if self.original_nick:
|
||
|
self.bot.send(f'NICK {self.original_nick}')
|
||
|
self.bot.nick = self.original_nick
|
||
|
self.bot.log.info(f"Nickname reverted to: {self.original_nick}")
|
||
|
return "Periodic tasks stopped and nickname reverted."
|
||
|
return "Permission denied."
|
||
|
|
||
|
@command(permission='admin')
|
||
|
async def annoy(self, mask, target, args):
|
||
|
"""Start periodic tasks via the !startannoy command.
|
||
|
|
||
|
%%annoy
|
||
|
"""
|
||
|
if mask.nick == self.bot.config.get('owner', ''):
|
||
|
if not self.running:
|
||
|
self.channel = target
|
||
|
self.start_periodic_tasks()
|
||
|
return "Annoy tasks started."
|
||
|
return "Permission denied."
|
||
|
|
||
|
def _cancel_tasks(self):
|
||
|
"""Cancel all running tasks."""
|
||
|
for task in self.tasks:
|
||
|
if task and not task.done():
|
||
|
task.cancel()
|
||
|
self.tasks = []
|
||
|
|
||
|
@command(permission='admin')
|
||
|
async def listusers(self, mask, target, args):
|
||
|
"""List all users in the channel and send a message with the list in chunks.
|
||
|
|
||
|
%%listusers
|
||
|
"""
|
||
|
self.channel = target
|
||
|
channel_key = self.channel.lower()
|
||
|
if channel_key in self.bot.channels:
|
||
|
users = list(self.bot.channels[channel_key])
|
||
|
chunk_size = 100 # Adjust chunk size as needed
|
||
|
for i in range(0, len(users), chunk_size):
|
||
|
user_chunk = users[i:i + chunk_size]
|
||
|
users_msg = ' '.join(user_chunk)
|
||
|
self.bot.privmsg(self.channel, f"{users_msg}")
|
||
|
await asyncio.sleep(0.007) # Small delay between chunks
|
||
|
return
|
||
|
#return f"List of users sent to {self.channel} in chunks."
|
||
|
else:
|
||
|
return f"Channel {self.channel} not found."
|