Greatly improve channel handling

This also does some more bits of rudimentary user handling, but most of it still remains to be done.

Note: This adds DEV_PREFIX to botconfig and LOG_PREFIX to settings (var), make sure to properly update your bot!
This commit is contained in:
Vgr E. Barry 2016-11-02 22:31:54 -04:00
parent 49208db148
commit d090e573b7
10 changed files with 217 additions and 281 deletions

View File

@ -35,7 +35,8 @@ DISABLE_DEBUG_MODE_TIME_LORD = False
ALT_CHANNELS = ""
ALLOWED_ALT_CHANNELS_COMMANDS = []
DEV_CHANNEL = ""
DEV_CHANNEL = "" # Important: Do *not* include the message prefix!
DEV_PREFIX = "" # The prefix to send to the dev channel (e.g. "+" will send to "+#dev-chan")
PASTEBIN_ERRORS = False # If DEV_CHANNEL is set, errors will be posted there.
LOG_CHANNEL = "" # Log !fwarns to this channel, if set

View File

@ -136,7 +136,6 @@
"ping_player": "PING! {0} player{1}! ",
"already_voted_game": "You have already voted for the {0} game mode.",
"vote_game_mode": "\u0002{0}\u0002 votes for the \u0002{1}\u0002 game mode.",
"bot_not_opped": "Sorry, I'm not opped in {0}.",
"already_playing": "{0}'re already playing!",
"too_many_players": "Too many players! Try again next time.",
"game_already_running": "Sorry, but the game is already running. Try again next time.",

View File

@ -7,6 +7,8 @@ from src.logger import debuglog
from src import users
Main = None # main channel
Dummy = None # fake channel
Dev = None # dev channel
_channels = {}
@ -175,7 +177,7 @@ class Channel(IRCContext):
if c in status_modes: # op/voice status; keep it here and update the user's registry too
if c not in self.modes:
self.modes[c] = set()
user = users.get(targets[i], allow_bot=True)
user = users._get(targets[i], allow_bot=True) # FIXME
self.modes[c].add(user)
user.channels[self].add(c)
i += 1
@ -199,7 +201,7 @@ class Channel(IRCContext):
else:
if c in status_modes:
if c in self.modes:
user = users.get(targets[i], allow_bot=True)
user = users._get(targets[i], allow_bot=True) # FIXME
self.modes[c].discard(user)
user.channels[self].discard(c)
if not self.modes[c]:
@ -241,10 +243,10 @@ class FakeChannel(Channel):
is_fake = True
def join(self, key=""):
pass # don't actually do anything
self.state = _States.Joined
def part(self, message=""):
pass
self.state = _States.Left
def send(self, data, *, notice=False, privmsg=False):
debuglog("Would message fake channel {0}: {1!r}".format(self.name, data))

View File

@ -40,6 +40,42 @@ class IRCContext:
return "NOTICE"
return "PRIVMSG"
@staticmethod
def _who(cli, target, data=b""):
"""Handle WHO requests."""
if isinstance(data, str):
data = data.encode(Features["CHARSET"])
elif isinstance(data, int):
if data > 0xFFFFFF:
data = b""
else:
data = data.to_bytes(3, "little")
if len(data) > 3:
data = b""
if "WHOX" in Features:
cli.send("WHO", target, b"%tcuihsnfdlar," + data)
else:
cli.send("WHO", target)
return int.from_bytes(data, "little")
def who(self, data=b""):
"""Send a WHO request with respect to the server's capabilities.
To get the WHO replies, add an event listener for "who_result",
and an event listener for "who_end" for the end of WHO replies.
The return value of this function is an integer equal to the data
given. If the server supports WHOX, the same integer will be in the
event.params.data attribute. Otherwise, this attribute will be 0.
"""
return self._who(self.client, self.name, data)
@staticmethod
def _send(data, client, send_type, name):
full_address = "{cli.nickname}!{cli.ident}@{cli.hostmask}".format(cli=client)

View File

@ -5,50 +5,32 @@ import socket
import sys
import traceback
from oyoyo.parse import parse_nick
import botconfig
import src.settings as var
from src import decorators, wolfgame, errlog as log, stream_handler as alog
from src.utilities import irc_equals
from src import decorators, wolfgame, channels, hooks, users, errlog as log, stream_handler as alog
hook = decorators.hook
def on_privmsg(cli, rawnick, chan, msg, *, notice=False):
try:
prefixes = getattr(var, "STATUSMSG_PREFIXES")
except AttributeError:
pass
else:
if botconfig.IGNORE_HIDDEN_COMMANDS and chan[0] in prefixes:
if notice and "!" not in rawnick or not rawnick: # server notice; we don't care about those
return
try:
getattr(var, "CASEMAPPING")
except AttributeError:
# some kind of hack for strange networks which don't put server name in some of the NOTICEs on startup
if not rawnick:
return
if notice and "!" not in rawnick and chan in ("*", "AUTH"):
# On-connect message before RPL_ISUPPORT is sent.
if chan != botconfig.NICK and botconfig.IGNORE_HIDDEN_COMMANDS and not chan.startswith(tuple(hooks.Features["CHANTYPES"])):
return
log("Server did not send a case mapping; falling back to rfc1459.")
var.CASEMAPPING = "rfc1459"
if (notice and ((not irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_NOTICE_COMMANDS) or
(irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
if (notice and ((not users.equals(chan, botconfig.NICK) and not botconfig.ALLOW_NOTICE_COMMANDS) or
(users.equals(chan, botconfig.NICK) and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
return # not allowed in settings
if irc_equals(chan, botconfig.NICK):
chan = parse_nick(rawnick)[0]
if users.equals(chan, botconfig.NICK):
chan = users.parse_rawnick_as_dict(rawnick)["nick"]
for fn in decorators.COMMANDS[""]:
fn.caller(cli, rawnick, chan, msg)
phase = var.PHASE
for x in list(decorators.COMMANDS.keys()):
if chan != parse_nick(rawnick)[0] and not msg.lower().startswith(botconfig.CMD_CHAR):
if chan != users.parse_rawnick_as_dict(rawnick)["nick"] and not msg.lower().startswith(botconfig.CMD_CHAR):
break # channel message but no prefix; ignore
if msg.lower().startswith(botconfig.CMD_CHAR+x):
h = msg[len(x)+len(botconfig.CMD_CHAR):]
@ -61,14 +43,9 @@ def on_privmsg(cli, rawnick, chan, msg, *, notice=False):
if phase == var.PHASE:
fn.caller(cli, rawnick, chan, h.lstrip())
def unhandled(cli, prefix, cmd, *args):
if cmd in decorators.HOOKS:
largs = list(args)
for i,arg in enumerate(largs):
if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
for fn in decorators.HOOKS.get(cmd, []):
fn.caller(cli, prefix, *largs)
fn.caller(cli, prefix, *args)
def connect_callback(cli):
@hook("endofmotd", hookid=294)
@ -76,6 +53,12 @@ def connect_callback(cli):
def prepare_stuff(cli, prefix, *args):
alog("Received end of MOTD from {0}".format(prefix))
# This callback only sets up event listeners
wolfgame.connect_callback()
users.Bot = users.User(cli, botconfig.NICK, None, None, None, None, {})
users.Bot.modes = set() # only for the bot (user modes)
# just in case we haven't managed to successfully auth yet
if not botconfig.SASL_AUTHENTICATION:
cli.ns_identify(botconfig.USERNAME or botconfig.NICK,
@ -83,26 +66,24 @@ def connect_callback(cli):
nickserv=var.NICKSERV,
command=var.NICKSERV_IDENTIFY_COMMAND)
channels = {botconfig.CHANNEL}
channels.Main = channels.add(botconfig.CHANNEL, cli)
channels.Dummy = channels.add("*", cli)
if botconfig.ALT_CHANNELS:
channels.update(botconfig.ALT_CHANNELS.split(","))
for chan in botconfig.ALT_CHANNELS.split(","):
channels.add(chan, cli)
if botconfig.DEV_CHANNEL:
channels.update(chan.lstrip("".join(var.STATUSMSG_PREFIXES)) for chan in botconfig.DEV_CHANNEL.split(","))
channels.Dev = channels.add(botconfig.DEV_CHANNEL, cli)
if var.LOG_CHANNEL:
channels.add(var.LOG_CHANNEL.lstrip("".join(var.STATUSMSG_PREFIXES)))
channels.add(var.LOG_CHANNEL, cli)
cli.join(",".join(channels))
if var.CHANSERV_OP_COMMAND:
cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL))
#if var.CHANSERV_OP_COMMAND: # TODO: Add somewhere else if needed
# cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL))
cli.nick(botconfig.NICK) # very important (for regain/release)
wolfgame.connect_callback(cli)
def mustregain(cli, *blah):
if not botconfig.PASS:
return
@ -181,10 +162,4 @@ def connect_callback(cli):
"in botconfig.USERNAME if it's different from the bot nick?")
cli.quit()
@hook("ping")
def on_ping(cli, prefix, server):
cli.send('PONG', server)
# vim: set sw=4 expandtab:

View File

@ -13,46 +13,9 @@ from src.logger import plog
from src import channels, users, settings as var
### WHO/WHOX requests and responses handling
### WHO/WHOX responses handling
def bare_who(cli, target, data=b""):
"""Handle WHO requests."""
if isinstance(data, str):
data = data.encode(Features["CHARSET"])
elif isinstance(data, int):
if data > 0xFFFFFF:
data = b""
else:
data = data.to_bytes(3, "little")
if len(data) > 3:
data = b""
if "WHOX" in Features:
cli.send("WHO", target, b"%tcuihsnfdlar," + data)
else:
cli.send("WHO", target)
return int.from_bytes(data, "little")
# Always use this function whenever sending out a WHO request!
def who(target, data=b""):
"""Send a WHO request with respect to the server's capabilities.
To get the WHO replies, add an event listener for "who_result", and
an event listener for "who_end" for the end of WHO replies.
The return value of this function is an integer equal to the data
given. If the server supports WHOX, the same integer will be in the
event.params.data attribute. Otherwise, this attribute will be 0.
"""
return bare_who(target.client, target.name, data)
#@hook("whoreply")
@hook("whoreply")
def who_reply(cli, bot_server, bot_nick, chan, ident, host, server, nick, status, hopcount_gecos):
"""Handle WHO replies for servers without WHOX support.
@ -95,6 +58,7 @@ def who_reply(cli, bot_server, bot_nick, chan, ident, host, server, nick, status
user = users._add(cli, nick=nick, ident=ident, host=host, realname=realname)
ch = channels.add(chan, cli)
if ch not in user.channels:
user.channels[ch] = modes
ch.users.add(user)
for mode in modes:
@ -105,7 +69,10 @@ def who_reply(cli, bot_server, bot_nick, chan, ident, host, server, nick, status
event = Event("who_result", {}, away=is_away, data=0, ip_address=None, server=server, hop_count=hop, idle_time=None, extended_who=False)
event.dispatch(var, ch, user)
#@hook("whospcrpl")
if ch is channels.Main and not users.exists(nick): # FIXME
users.add(nick, ident=ident,host=host,account="*",inchan=True,modes=modes,moded=set())
@hook("whospcrpl")
def extended_who_reply(cli, bot_server, bot_nick, data, chan, ident, ip_address, host, server, nick, status, hop, idle, account, realname):
"""Handle WHOX responses for servers that support it.
@ -164,6 +131,7 @@ def extended_who_reply(cli, bot_server, bot_nick, data, chan, ident, ip_address,
user = users._add(cli, nick=nick, ident=ident, host=host, realname=realname, account=account)
ch = channels.add(chan, cli)
if ch not in user.channels:
user.channels[ch] = modes
ch.users.add(user)
for mode in modes:
@ -174,7 +142,10 @@ def extended_who_reply(cli, bot_server, bot_nick, data, chan, ident, ip_address,
event = Event("who_result", {}, away=is_away, data=data, ip_address=ip_address, server=server, hop_count=hop, idle_time=idle, extended_who=True)
event.dispatch(var, ch, user)
#@hook("endofwho")
if ch is channels.Main and not users.exists(nick): # FIXME
users.add(nick, ident=ident,host=host,account=account,inchan=True,modes=modes,moded=set())
@hook("endofwho")
def end_who(cli, bot_server, bot_nick, target, rest):
"""Handle the end of WHO/WHOX responses from the server.
@ -196,7 +167,7 @@ def end_who(cli, bot_server, bot_nick, target, rest):
### Server PING handling
#@hook("ping")
@hook("ping")
def on_ping(cli, prefix, server):
"""Send out PONG replies to the server's PING requests.
@ -208,7 +179,7 @@ def on_ping(cli, prefix, server):
"""
with cli: # TODO: Make the client a context manager for this to work
with cli:
cli.send("PONG", server)
### Fetch and store server information
@ -266,7 +237,7 @@ def get_features(cli, rawnick, *features):
### Channel and user MODE handling
#@hook("channelmodeis")
@hook("channelmodeis")
def current_modes(cli, server, bot_nick, chan, mode, *targets):
"""Update the channel modes with the existing ones.
@ -284,7 +255,7 @@ def current_modes(cli, server, bot_nick, chan, mode, *targets):
ch = channels.add(chan, cli)
ch.update_modes(server, mode, targets)
#@hook("channelcreate")
@hook("channelcreate")
def chan_created(cli, server, bot_nick, chan, timestamp):
"""Update the channel timestamp with the server's information.
@ -303,7 +274,7 @@ def chan_created(cli, server, bot_nick, chan, timestamp):
channels.add(chan, cli).timestamp = int(timestamp)
#@hook("mode")
@hook("mode")
def mode_change(cli, rawnick, chan, mode, *targets):
"""Update the channel and user modes whenever a mode change occurs.
@ -320,7 +291,7 @@ def mode_change(cli, rawnick, chan, mode, *targets):
"""
actor = users._get(rawnick, allow_none=True, raw_nick=True) # FIXME
actor = users._get(rawnick, allow_none=True) # FIXME
if chan == users.Bot.nick: # we only see user modes set to ourselves
users.Bot.modes.update(mode)
return
@ -335,7 +306,6 @@ def mode_change(cli, rawnick, chan, mode, *targets):
def handle_listmode(cli, chan, mode, target, setter, timestamp):
"""Handle and store list modes."""
return # FIXME
ch = channels.add(chan, cli)
if mode not in ch.modes:
ch.modes[mode] = {}
@ -417,7 +387,6 @@ def check_inviteexemptlist(cli, server, bot_nick, chan, target, setter, timestam
def handle_endlistmode(cli, chan, mode):
"""Handle the end of a list mode listing."""
return # FIXME
ch = channels.add(chan, cli)
Event("end_listmode", {}).dispatch(var, ch, mode)
@ -488,7 +457,7 @@ def end_inviteexemptlist(cli, server, bot_nick, chan, message):
### NICK handling
#@hook("nick")
@hook("nick")
def on_nick_change(cli, old_nick, nick):
"""Handle a user changing nicks, which may be the bot itself.
@ -507,7 +476,7 @@ def on_nick_change(cli, old_nick, nick):
### JOIN handling
#@hook("join")
@hook("join")
def join_chan(cli, rawnick, chan, account=None, realname=None):
"""Handle a user joining a channel, which may be the bot itself.
@ -533,19 +502,19 @@ def join_chan(cli, rawnick, chan, account=None, realname=None):
realname = None
ch = channels.add(chan, cli)
ch.state = 2
ch.state = channels._States.Joined
if users.parse_rawnick_as_dict(rawnick)["nick"] == users.Bot.nick: # we may not be fully set up yet
ch.mode()
ch.mode(Features["CHANMODES"][0])
who(ch)
ch.who()
user = users.Bot
else:
try: # FIXME
user = users._get(rawnick, account=account, realname=realname, raw_nick=True)
user = users._get(rawnick, account=account, realname=realname)
except KeyError:
user = users._add(cli, nick=rawnick, account=account, realname=realname, raw_nick=True)
user = users._add(cli, nick=rawnick, account=account, realname=realname)
ch.users.add(user)
user.channels[ch] = set()
@ -554,7 +523,7 @@ def join_chan(cli, rawnick, chan, account=None, realname=None):
### PART handling
#@hook("part")
@hook("part")
def part_chan(cli, rawnick, chan, reason=""):
"""Handle a user leaving a channel, which may be the bot itself.
@ -571,7 +540,7 @@ def part_chan(cli, rawnick, chan, reason=""):
"""
ch = channels.add(chan, cli)
user = users._get(rawnick, allow_bot=True, raw_nick=True) # FIXME
user = users._get(rawnick, allow_bot=True) # FIXME
if user is users.Bot: # oh snap! we're no longer in the channel!
ch._clear()
@ -582,7 +551,7 @@ def part_chan(cli, rawnick, chan, reason=""):
### KICK handling
#@hook("kick")
@hook("kick")
def kicked_from_chan(cli, rawnick, chan, target, reason):
"""Handle a user being kicked from a channel.
@ -597,13 +566,13 @@ def kicked_from_chan(cli, rawnick, chan, target, reason):
"""
ch = channels.add(chan, cli)
actor = users._get(rawnick, allow_bot=True, raw_nick=True) # FIXME
actor = users._get(rawnick, allow_bot=True) # FIXME
user = users._get(target, allow_bot=True) # FIXME
if user is users.Bot:
ch._clear()
else:
ch.remove_user(val)
ch.remove_user(user)
Event("chan_kick", {}).dispatch(var, ch, actor, user, reason)
@ -618,10 +587,10 @@ def quit(context, message=""):
plog("Tried to QUIT but everything was being torn down.")
return
with cli: # TODO: Make the client into a context manager
with cli:
cli.send("QUIT :{0}".format(message))
#@hook("quit")
@hook("quit")
def on_quit(cli, rawnick, reason):
"""Handle a user quitting the IRC server.
@ -639,7 +608,7 @@ def on_quit(cli, rawnick, reason):
"""
user = users._get(rawnick, allow_bot=True, raw_nick=True) # FIXME
user = users._get(rawnick, allow_bot=True) # FIXME
for chan in set(user.channels):
if user is users.Bot:
@ -648,3 +617,5 @@ def on_quit(cli, rawnick, reason):
chan.remove_user(user)
Event("server_quit", {}).dispatch(var, user, reason)
# vim: set sw=4 expandtab:

View File

@ -199,6 +199,7 @@ CHANSERV_OP_COMMAND = "OP {channel}"
GUEST_NICK_PATTERN = r"^Guest\d+$|^\d|away.+|.+away"
LOG_CHANNEL = "" # Log !fwarns to this channel, if set
LOG_PREFIX = "" # Message prefix for LOG_CHANNEL
# TODO: move this to a game mode called "fixed" once we implement a way to randomize roles (and have that game mode be called "random")
DEFAULT_ROLE = "villager"

View File

@ -3,7 +3,7 @@ import re
import botconfig
import src.settings as var
from src import db
from src import channels, db
from src.utilities import *
from src.decorators import cmd
from src.events import Event
@ -46,14 +46,14 @@ def decrement_stasis(nick=None):
db.expire_stasis()
db.init_vars()
def expire_tempbans(cli):
def expire_tempbans():
acclist, hmlist = db.expire_tempbans()
cmodes = []
for acc in acclist:
cmodes.append(("-b", "{0}{1}".format(var.ACCOUNT_PREFIX, acc)))
for hm in hmlist:
cmodes.append(("-b", "*!*@{0}".format(hm)))
mass_mode(cli, cmodes, [])
channels.Main.mode(*cmodes)
def parse_warning_target(target, lower=False):
if target[0] == "=":
@ -677,7 +677,7 @@ def fwarn(cli, nick, chan, rest):
reply(cli, nick, chan, messages["fwarn_done"])
if var.LOG_CHANNEL:
cli.msg(var.LOG_CHANNEL, messages["fwarn_log_del"].format(warn_id, warning["target"], hm,
cli.msg(var.LOG_PREFIX + var.LOG_CHANNEL, messages["fwarn_log_del"].format(warn_id, warning["target"], hm,
warning["reason"], (" | " + warning["notes"]) if warning["notes"] else ""))
return

View File

@ -46,7 +46,7 @@ import botconfig
import src
import src.settings as var
from src.utilities import *
from src import db, decorators, events, users, logger, proxy, debuglog, errlog, plog
from src import db, decorators, events, channels, users, hooks, logger, proxy, debuglog, errlog, plog
from src.decorators import cmd, hook, handle_error, event_listener, COMMANDS
from src.messages import messages
from src.warnings import *
@ -90,7 +90,7 @@ var.DISCONNECTED = {} # players who got disconnected
var.RESTARTING = False
var.OPPED = False # Keeps track of whether the bot is opped
#var.OPPED = False # Keeps track of whether the bot is opped
var.BITTEN_ROLES = {}
var.LYCAN_ROLES = {}
@ -124,7 +124,7 @@ if botconfig.DEBUG_MODE and var.DISABLE_DEBUG_MODE_TIME_LORD:
plog("Loading Werewolf IRC bot")
def connect_callback(cli):
def connect_callback():
db.init_vars()
SIGUSR1 = getattr(signal, "SIGUSR1", None)
SIGUSR2 = getattr(signal, "SIGUSR2", None)
@ -151,113 +151,58 @@ def connect_callback(cli):
if SIGUSR2:
signal.signal(SIGUSR2, sighandler)
to_be_devoiced = []
cmodes = []
@hook("quietlist", hookid=294)
def on_quietlist(cli, server, botnick, channel, q, quieted, by, something):
if re.search(r"^{0}.+\!\*@\*$".format(var.QUIET_PREFIX), quieted): # only unquiet people quieted by bot
cmodes.append(("-{0}".format(var.QUIET_MODE), quieted))
@hook("banlist", hookid=294)
def on_banlist(cli, server, botnick, channel, ban, by, timestamp):
if re.search(r"^{0}.+\!\*@\*$".format(var.QUIET_PREFIX), ban):
cmodes.append(("-{0}".format(var.QUIET_MODE), ban))
@hook("whoreply", hookid=295)
def on_whoreply(cli, svr, botnick, chan, user, host, server, nick, status, rest):
def who_end(event, var, request):
if request == channels.Main.name:
if "WHOX" not in hooks.Features:
if not var.DISABLE_ACCOUNTS:
plog("IRCd does not support accounts, disabling account-related features.")
var.DISABLE_ACCOUNTS = True
var.ACCOUNTS_ONLY = False
if users.exists(nick, user, host):
return
if nick == botconfig.NICK:
cli.nickname = nick
cli.ident = user
cli.hostmask = host
if "+" in status:
to_be_devoiced.append(nick)
newstat = ""
for stat in status:
if not stat in var.MODES_PREFIXES:
continue
newstat += var.MODES_PREFIXES[stat]
users.add(nick, ident=user,host=host,account="*",inchan=True,modes=set(newstat),moded=set())
@hook("whospcrpl", hookid=295)
def on_whoreply(cli, server, nick, ident, host, _, user, status, acc):
if users.exists(user, ident, host):
return # Don't add someone who is already there
if user == botconfig.NICK:
cli.nickname = user
cli.ident = ident
cli.hostmask = host
if acc == "0":
acc = "*"
if "+" in status:
to_be_devoiced.append(user)
newstat = ""
for stat in status:
if not stat in var.MODES_PREFIXES:
continue
newstat += var.MODES_PREFIXES[stat]
users.add(user, ident=ident,host=host,account=acc,inchan=True,modes=set(newstat),moded=set())
@hook("endofwho", hookid=295)
def afterwho(*args):
# Devoice all on connect
for nick in to_be_devoiced:
cmodes.append(("-v", nick))
prefix = "-" + hooks.Features["PREFIX"]["+"]
pending = []
for user in channels.Main.modes.get(prefix, ()):
pending.append((prefix, user.nick))
accumulator.send(pending)
next(accumulator, None)
# Expire tempbans
expire_tempbans(cli)
expire_tempbans()
# If the bot was restarted in the middle of the join phase, ping players that were joined.
players = db.get_pre_restart_state()
if players:
msg = "PING! " + break_long_message(players).replace("\n", "\nPING! ")
cli.msg(botconfig.CHANNEL, msg)
cli.msg(botconfig.CHANNEL, messages["game_restart_cancel"])
channel.Main.send(msg)
channel.Main.send(messages["game_restart_cancel"])
# Unhook the WHO hooks
hook.unhook(295)
events.remove_listener("who_end", who_end)
def end_listmode(event, var, chan, mode):
if chan is channels.Main and mode == var.QUIET_MODE:
pending = []
for quiet in chan.modes.get(mode, ()):
if re.search(r"^{0}.+\!\*@\*$".format(var.QUIET_PREFIX), quiet):
pending.append(("-" + mode, quiet))
accumulator.send(pending)
next(accumulator, None)
#bot can be tricked into thinking it's still opped by doing multiple modes at once
@hook("mode", hookid=296)
def on_give_me_ops(cli, nick, chan, modeaction, target="", *other):
if chan != botconfig.CHANNEL:
return
if modeaction == "+o" and target == botconfig.NICK:
var.OPPED = True
if users.exists(botconfig.NICK):
users.get(botconfig.NICK).modes.add("o")
events.remove_listener("end_listmode", end_listmode)
if var.PHASE == "none":
@hook("quietlistend", hookid=297)
def on_quietlist_end(cli, svr, nick, chan, *etc):
if chan == botconfig.CHANNEL:
mass_mode(cli, cmodes, ["-m"])
@hook("endofbanlist", hookid=297)
def on_banlist_end(cli, svr, nick, chan, *etc):
if chan == botconfig.CHANNEL:
mass_mode(cli, cmodes, ["-m"])
events.add_listener("who_end", who_end)
events.add_listener("end_listmode", end_listmode)
cli.mode(botconfig.CHANNEL, var.QUIET_MODE) # unquiet all
elif modeaction == "-o" and target == botconfig.NICK:
var.OPPED = False
if var.CHANSERV_OP_COMMAND:
cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL))
def accumulate_cmodes(count):
modes = []
for i in range(count):
item = yield
modes.extend(item)
yield i
channel.Main.mode(*modes)
if var.DISABLE_ACCOUNTS:
cli.who(botconfig.CHANNEL)
else:
cli.who(botconfig.CHANNEL, "%uhsnfa")
accumulator = accumulate_cmodes(2)
accumulator.send(None)
@hook("mode") # XXX Get rid of this when the user/channel refactor is done
def check_for_modes(cli, rnick, chan, modeaction, *target):
@ -308,6 +253,10 @@ def check_for_modes(cli, rnick, chan, modeaction, *target):
if "!" not in rnick:
sync_modes(cli)
@cmd("break")
def break_stuff(*s):
1/0
def reset_settings():
var.CURRENT_GAMEMODE.teardown()
var.CURRENT_GAMEMODE = var.GAME_MODES["default"][0]()
@ -400,7 +349,7 @@ def refreshdb(cli, nick, chan, rest):
"""Updates our tracking vars to the current db state."""
db.expire_stasis()
db.init_vars()
expire_tempbans(cli)
expire_tempbans()
reply(cli, nick, chan, "Done.")
@cmd("die", "bye", "fdie", "fbye", flag="D", pm=True)
@ -875,9 +824,9 @@ def join_timer_handler(cli):
return
@hook("whoreply", hookid=387)
def ping_altpingers_noacc(cli, svr, botnick, chan, ident, host, server, nick, status, rest):
def ping_altpingers_noacc(cli, bot_server, bot_nick, chan, ident, host, server, nick, status, hopcount_gecos):
if ("G" in status or is_user_stasised(nick) or not var.PINGING_IFS or
nick == botnick or nick in pl):
nick == bot_nick or nick in pl):
return
ident = irc_lower(ident)
@ -888,24 +837,24 @@ def join_timer_handler(cli):
var.PINGED_ALREADY.add(hostmask)
@hook("whospcrpl", hookid=387)
def ping_altpingers(cli, server, nick, ident, host, _, user, status, acc):
if ("G" in status or is_user_stasised(user) or not var.PINGING_IFS or
user == botconfig.NICK or user in pl):
def ping_altpingers(cli, bot_server, bot_nick, data, chan, ident, ip_address, host, server, nick, status, hop, idle, account, realname):
if ("G" in status or is_user_stasised(nick) or not var.PINGING_IFS or
nick == botconfig.NICK or nick in pl):
return
# Create list of players to ping
acc = irc_lower(acc)
account = irc_lower(account)
ident = irc_lower(ident)
host = host.lower()
if acc and acc != "*":
if acc in chk_acc:
to_ping.append(user)
var.PINGED_ALREADY_ACCS.add(acc)
if account and account != "*":
if account in chk_acc:
to_ping.append(nick)
var.PINGED_ALREADY_ACCS.add(account)
elif not var.ACCOUNTS_ONLY:
hostmask = ident + "@" + host
to_ping.append(user)
to_ping.append(nick)
var.PINGED_ALREADY.add(hostmask)
@hook("endofwho", hookid=387)
@ -926,8 +875,9 @@ def join_timer_handler(cli):
cli.msg(botconfig.CHANNEL, msg)
# FIXME
if not var.DISABLE_ACCOUNTS:
cli.who(botconfig.CHANNEL, "%uhsnfa")
cli.who(botconfig.CHANNEL, "%tcuihsnfdlar,")
else:
cli.who(botconfig.CHANNEL)
@ -1067,12 +1017,6 @@ def join_player(cli, player, chan, who=None, forced=False, *, sanity=True):
if chan != botconfig.CHANNEL:
return False
if not var.OPPED:
cli.notice(who, messages["bot_not_opped"].format(chan))
if var.CHANSERV_OP_COMMAND:
cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL))
return False
if users.exists(player):
ident = irc_lower(users.get(player).ident)
host = users.get(player).host.lower()
@ -1222,7 +1166,7 @@ def kill_join(cli, chan):
# use this opportunity to expire pending stasis
db.expire_stasis()
db.init_vars()
expire_tempbans(cli)
expire_tempbans()
if var.AFTER_FLASTGAME is not None:
var.AFTER_FLASTGAME()
var.AFTER_FLASTGAME = None
@ -1241,11 +1185,6 @@ def fjoin(cli, nick, chan, rest):
return
noticed = False
fake = False
if not var.OPPED:
cli.notice(nick, messages["bot_not_opped"].format(chan))
if var.CHANSERV_OP_COMMAND:
cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL))
return
if not rest.strip():
evt.data["join_player"](cli, nick, chan, forced=True)
@ -2565,7 +2504,7 @@ def stop_game(cli, winner="", abort=False, additional_winners=None, log=True):
reset_modes_timers(cli)
reset()
expire_tempbans(cli)
expire_tempbans()
# This must be after reset()
if var.AFTER_FLASTGAME is not None:
@ -3283,15 +3222,15 @@ def on_join(cli, raw_nick, chan, acc="*", rname=""):
if nick in var.DCED_PLAYERS.keys():
var.PLAYERS[nick] = var.DCED_PLAYERS.pop(nick)
if nick == botconfig.NICK:
var.OPPED = False
#var.OPPED = False
cli.send("NAMES " + chan)
if nick == var.CHANSERV and not var.OPPED and var.CHANSERV_OP_COMMAND:
cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL))
#if nick == var.CHANSERV and not var.OPPED and var.CHANSERV_OP_COMMAND:
# cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL))
@hook("namreply")
def on_names(cli, _, __, *names):
if "@" + botconfig.NICK in names:
var.OPPED = True
#@hook("namreply")
#def on_names(cli, _, __, *names):
# if "@" + botconfig.NICK in names:
# var.OPPED = True
@cmd("goat", playing=True, phases=("day",))
def goat(cli, nick, chan, rest):
@ -6787,17 +6726,16 @@ def show_admins(cli, nick, chan, rest):
var.ADMIN_PINGING = True
@hook("whoreply", hookid=4)
def on_whoreply(cli, server, _, chan, ident, host, ___, user, status, ____):
if not var.ADMIN_PINGING:
def admin_whoreply(event, var, chan, user):
if not var.ADMIN_PINGING or chan is not channels.Main:
return
if is_admin(user) and "G" not in status and user != botconfig.NICK:
admins.append(user)
if is_admin(user.nick): # FIXME: Using the old interface for now; user.is_admin() is better
if user is not users.Bot and not event.params.away:
admins.append(user.nick) # FIXME
@hook("endofwho", hookid=4)
def show(*args):
if not var.ADMIN_PINGING:
def admin_endwho(event, var, target):
if not var.ADMIN_PINGING or target != channels.Main.name:
return
admins.sort(key=str.lower)
@ -6806,10 +6744,15 @@ def show_admins(cli, nick, chan, rest):
reply(cli, nick, chan, msg)
hook.unhook(4)
var.ADMIN_PINGING = False
cli.who(botconfig.CHANNEL)
events.remove_listener("who_result", admin_whoreply)
events.remove_listener("who_end", admin_endwho)
events.add_listener("who_result", admin_whoreply)
events.add_listener("who_end", admin_endwho)
channels.Main.who()
@cmd("coin", pm=True)
def coin(cli, nick, chan, rest):

View File

@ -45,6 +45,14 @@ except ImportError:
"", "- The lykos developers", sep="\n")
sys.exit(1)
try: # FIXME
botconfig.DEV_PREFIX
except AttributeError:
print("Please set up your config to include a DEV_PREFIX variable",
"If you have a prefix in your DEV_CHANNEL config, move it out into DEV_PREFIX",
sep="\n")
sys.exit(1)
from oyoyo.client import IRCClient
import src