From b10c34f3ac72714c43e29b54650faa0951c995ee Mon Sep 17 00:00:00 2001 From: linuxdaemon Date: Fri, 30 Nov 2018 16:30:16 -0600 Subject: [PATCH] Remove broken monsterhunt plugin --- plugins/monsterhunt.py | 622 ----------------------------------------- 1 file changed, 622 deletions(-) delete mode 100644 plugins/monsterhunt.py diff --git a/plugins/monsterhunt.py b/plugins/monsterhunt.py deleted file mode 100644 index f5edaf5a7..000000000 --- a/plugins/monsterhunt.py +++ /dev/null @@ -1,622 +0,0 @@ -import json -import operator -import random -from collections import defaultdict -from time import time - -from sqlalchemy import Table, Column, String, Integer, PrimaryKeyConstraint, desc -from sqlalchemy.sql import select - -from cloudbot import hook -from cloudbot.util import database, web - -duck = [" ε=ε=ε=ε=ε=┌(; ・_・)┘ ", " ε=ε=ε=ε=ε=ε=┌(๑ʘ∀ʘ)┘ ", " ===≡≡≡。゚┌(゚´Д`゚)┘゚。 ", " ・・・・・・・ᕕ(╯°□°)ᕗ "] -duck_tail = ["[¬º-°]¬", "(▼皿▼)", "←~∋(。Ψ▼ー▼)∈", "∋━━o(`∀´oメ)~→", "(˼●̙̂ ̟ ̟̎ ̟ ̘●̂˻)", "(;´༎ຶД༎ຶ`)", "(((༼•̫͡•༽)))", - "( ͡° ͜◯ ͡°)"] -duck_noise = ["RUN!!!!!", "AHHHHHHHHHHH", "FFFFFUUUUUUU!", "THE CLOWNS ARE COMING!!!!!"] - -table = Table( - 'monster_hunt', - database.metadata, - Column('network', String), - Column('name', String), - Column('shot', Integer), - Column('befriend', Integer), - Column('chan', String), - PrimaryKeyConstraint('name', 'chan', 'network') -) - -optout = Table( - 'monster_nohunt', - database.metadata, - Column('network', String), - Column('chan', String), - PrimaryKeyConstraint('chan', 'network') -) - -""" -game_status structure -{ - 'network':{ - '#chan1':{ - 'duck_status':0|1|2, - 'next_duck_time':'integer', - 'game_started':0|1, - 'no_duck_kick': 0|1, - 'duck_time': 'float', - 'shoot_time': 'float' - } - } -} -""" - -MSG_DELAY = 10 -MASK_REQ = 3 -scripters = defaultdict(int) -game_status = defaultdict(lambda: defaultdict(lambda: defaultdict(int))) - - -# @hook.on_start() -def load_optout(db): - """load a list of channels duckhunt should be off in. Right now I am being lazy and not - differentiating between networks this should be cleaned up later.""" - global opt_out - opt_out = [] - chans = db.execute(select([optout.c.chan])) - if chans: - for row in chans: - chan = row["chan"] - opt_out.append(chan) - - -# @hook.event([EventType.message, EventType.action], singlethread=True) -def incrementMsgCounter(event, conn): - """Increment the number of messages said in an active game channel. Also keep track of the unique masks that are speaking.""" - global game_status - if event.chan in opt_out: - return - if game_status[conn.name][event.chan]['game_on'] == 1 and game_status[conn.name][event.chan]['duck_status'] == 0: - game_status[conn.name][event.chan]['messages'] += 1 - if event.host not in game_status[conn.name][event.chan]['masks']: - game_status[conn.name][event.chan]['masks'].append(event.host) - - -# @hook.command("starthunt", autohelp=False) -def start_hunt(chan, message, conn): - """This command starts a spooky MONSTER hunt in your channel, to stop the hunt use .stophunt""" - global game_status - if chan in opt_out: - return - elif not chan.startswith("#"): - return "No hunting by yourself, that isn't safe." - check = game_status[conn.name][chan]['game_on'] - if check: - return "There is already a hunt running in {}.".format(chan) - else: - game_status[conn.name][chan]['game_on'] = 1 - set_ducktime(chan, conn) - message( - "Monsters have been spotted chasing people. Save the person and kill the monster with .bang, or try and befriend the monster and let it kill the person using .befriend. For more information on this creepy event see https://redd.it/56sqlx", - chan) - - -def set_ducktime(chan, conn): - global game_status - game_status[conn.name][chan]['next_duck_time'] = random.randint(int(time()) + 480, int(time()) + 3600) - # game_status[conn.name][chan]['flyaway'] = game_status[conn.name][chan]['next_duck_time'] + 600 - game_status[conn.name][chan]['duck_status'] = 0 - # let's also reset the number of messages said and the list of masks that have spoken. - game_status[conn.name][chan]['messages'] = 0 - game_status[conn.name][chan]['masks'] = [] - return - - -# @hook.command("stophunt", autohelp=False) -def stop_hunt(chan, conn): - """This command stops the Monster hunt in your channel. Scores will be preserved""" - global game_status - if chan in opt_out: - return - if game_status[conn.name][chan]['game_on']: - game_status[conn.name][chan]['game_on'] = 0 - return "The hunt has been stopped." - else: - return "There is no monster hunt running in {}.".format(chan) - - -# @hook.command("monsterkick") -def no_duck_kick(text, chan, conn, notice): - """If the bot has OP or half-op in the channel you can specify .monsterkick enable|disable so that people are kicked for shooting or befriending a non-existent monster. Default is off.""" - global game_status - if chan in opt_out: - return - if text.lower() == 'enable': - game_status[conn.name][chan]['no_duck_kick'] = 1 - return "users will now be kicked for shooting or befriending non-existent monsters. The bot needs to have appropriate flags to be able to kick users for this to work." - elif text.lower() == 'disable': - game_status[conn.name][chan]['no_duck_kick'] = 0 - return "kicking for non-existent monsters has been disabled." - else: - notice(no_duck_kick.__doc__) - return - - -def generate_duck(): - """Try and randomize the monster message so people can't highlight on it/script against it.""" - dtail = random.choice(duck_tail) - rt = random.randint(1, len(dtail) - 1) - dtail = dtail[:rt] + u' \u200b ' + dtail[rt:] - dbody = random.choice(duck) - rb = random.randint(1, len(dbody) - 1) - dbody = dbody[:rb] + u'\u200b' + dbody[rb:] - dnoise = random.choice(duck_noise) - rn = random.randint(1, len(dnoise) - 1) - dnoise = dnoise[:rn] + u'\u200b' + dnoise[rn:] - return (dtail, dbody, dnoise) - - -# @hook.periodic(11, initial_interval=11) -def deploy_duck(bot): - global game_status - for network in game_status: - if network not in bot.connections: - continue - conn = bot.connections[network] - if not conn.ready: - continue - for chan in game_status[network]: - active = game_status[network][chan]['game_on'] - duck_status = game_status[network][chan]['duck_status'] - next_duck = game_status[network][chan]['next_duck_time'] - chan_messages = game_status[network][chan]['messages'] - chan_masks = game_status[network][chan]['masks'] - if active == 1 and duck_status == 0 and next_duck <= time() and chan_messages >= MSG_DELAY and len( - chan_masks) >= MASK_REQ: - # deploy a duck to channel - game_status[network][chan]['duck_status'] = 1 - game_status[network][chan]['duck_time'] = time() - dtail, dbody, dnoise = generate_duck() - conn.message(chan, "{}{}{}".format(dtail, dbody, dnoise)) - # Leave this commented out for now. I haven't decided how to make ducks leave. - # if active == 1 and duck_status == 1 and game_status[network][chan]['flyaway'] <= int(time()): - # conn.message(chan, "The duck flew away.") - # game_status[network][chan]['duck_status'] = 2 - # set_ducktime(chan, conn) - continue - continue - - -def hit_or_miss(deploy, shoot): - """This function calculates if the befriend or bang will be successful.""" - if shoot - deploy < 1: - return .05 - elif 1 <= shoot - deploy <= 7: - out = random.uniform(.60, .75) - return out - else: - return 1 - - -def dbadd_entry(nick, chan, db, conn, shoot, friend): - """Takes care of adding a new row to the database.""" - query = table.insert().values( - network=conn.name, - chan=chan.lower(), - name=nick.lower(), - shot=shoot, - befriend=friend) - db.execute(query) - db.commit() - - -def dbupdate(nick, chan, db, conn, shoot, friend): - """update a db row""" - if shoot and not friend: - query = table.update() \ - .where(table.c.network == conn.name) \ - .where(table.c.chan == chan.lower()) \ - .where(table.c.name == nick.lower()) \ - .values(shot=shoot) - db.execute(query) - db.commit() - elif friend and not shoot: - query = table.update() \ - .where(table.c.network == conn.name) \ - .where(table.c.chan == chan.lower()) \ - .where(table.c.name == nick.lower()) \ - .values(befriend=friend) - db.execute(query) - db.commit() - elif friend and shoot: - query = table.update() \ - .where(table.c.network == conn.name) \ - .where(table.c.chan == chan.lower()) \ - .where(table.c.name == nick.lower()) \ - .values(befriend=friend) \ - .values(shot=shoot) - db.execute(query) - db.commit() - - -# @hook.command("bang", autohelp=False) -def bang(nick, chan, message, db, conn, notice): - """when there is a monster chasing someone use this command to shoot it.""" - global game_status, scripters - if chan in opt_out: - return - network = conn.name - out = "" - miss = ["WHOOSH! You totally missed the monster. Save the poor human!", "Your gun jammed!", - "Better luck next time.", "Good thing this is just an IRC game cus your aim is terrible!"] - if not game_status[network][chan]['game_on']: - return "There is no activehunt right now. Use .starthunt to start a game." - elif game_status[network][chan]['duck_status'] != 1: - if game_status[network][chan]['no_duck_kick'] == 1: - out = "KICK {} {} There is no monster! What are you shooting at?".format(chan, nick) - conn.send(out) - return - return "There is no monster. What are you shooting at?" - else: - game_status[network][chan]['shoot_time'] = time() - deploy = game_status[network][chan]['duck_time'] - shoot = game_status[network][chan]['shoot_time'] - if nick.lower() in scripters: - if scripters[nick.lower()] > shoot: - notice("You are in a cool down period, you can try again in {} seconds.".format( - str(scripters[nick.lower()] - shoot))) - return - chance = hit_or_miss(deploy, shoot) - if not random.random() <= chance and chance > .05: - out = random.choice(miss) + " You can try again in 7 seconds." - scripters[nick.lower()] = shoot + 7 - return out - if chance == .05: - out += "You pulled the trigger in {} seconds, that's mighty fast. Are you sure you aren't a script? Take a 2 hour cool down.".format( - str(shoot - deploy)) - scripters[nick.lower()] = shoot + 7200 - if not random.random() <= chance: - return random.choice(miss) + " " + out - else: - message(out) - game_status[network][chan]['duck_status'] = 2 - score = db.execute(select([table.c.shot]) \ - .where(table.c.network == conn.name) \ - .where(table.c.chan == chan.lower()) \ - .where(table.c.name == nick.lower())).fetchone() - if score: - score = score[0] - score += 1 - dbupdate(nick, chan, db, conn, score, 0) - else: - score = 1 - dbadd_entry(nick, chan, db, conn, score, 0) - timer = "{:.3f}".format(shoot - deploy) - duck = "monster" if score == 1 else "monsters" - message( - "{} you shot a monster and saved a human in {} seconds! You have killed {} {} in {}.".format(nick, timer, - score, duck, - chan)) - set_ducktime(chan, conn) - - -# @hook.command("befriend", autohelp=False) -def befriend(nick, chan, message, db, conn, notice): - """when there is a monster on the loose chasing a human use this command to befriend it before someone else shoots it. This will also let it kill the human.""" - global game_status, scripters - if chan in opt_out: - return - network = conn.name - out = "" - miss = ["The monster didn't want to be friends, you are lucky they didn't eat you too!", - "The monster just grunted and kept chasing his prey, maybe you should provide an offering of some kind", - "Maybe your breath smells too sweet or your face looks too nice. Either way the monster rejected you."] - if not game_status[network][chan]['game_on']: - return "There is no hunt right now. Use .starthunt to start a game." - elif game_status[network][chan]['duck_status'] != 1: - if game_status[network][chan]['no_duck_kick'] == 1: - out = "KICK {} {} You tried befriending a non-existent monster, that's fucking creepy.".format(chan, nick) - conn.send(out) - return - return "You tried befriending a non-existent monster, that's fucking creepy." - else: - game_status[network][chan]['shoot_time'] = time() - deploy = game_status[network][chan]['duck_time'] - shoot = game_status[network][chan]['shoot_time'] - if nick.lower() in scripters: - if scripters[nick.lower()] > shoot: - notice("You are in a cool down period, you can try again in {} seconds.".format( - str(scripters[nick.lower()] - shoot))) - return - chance = hit_or_miss(deploy, shoot) - if not random.random() <= chance and chance > .05: - out = random.choice(miss) + " You can try again in 7 seconds." - scripters[nick.lower()] = shoot + 7 - return out - if chance == .05: - out += "You tried friending that monster in {} seconds, that's mighty fast. Are you sure you aren't a script? Take a 2 hour cool down.".format( - str(shoot - deploy)) - scripters[nick.lower()] = shoot + 7200 - if not random.random() <= chance: - return random.choice(miss) + " " + out - else: - message(out) - - game_status[network][chan]['duck_status'] = 2 - score = db.execute(select([table.c.befriend]) \ - .where(table.c.network == conn.name) \ - .where(table.c.chan == chan.lower()) \ - .where(table.c.name == nick.lower())).fetchone() - if score: - score = score[0] - score += 1 - dbupdate(nick, chan, db, conn, 0, score) - else: - score = 1 - dbadd_entry(nick, chan, db, conn, 0, score) - duck = "monster" if score == 1 else "monsters" - timer = "{:.3f}".format(shoot - deploy) - message("{} you befriended a monster in {} seconds! You have made friends with {} {} in {}.".format(nick, timer, - score, duck, - chan)) - set_ducktime(chan, conn) - - -def smart_truncate(content, length=320, suffix='...'): - if len(content) <= length: - return content - else: - return content[:length].rsplit(' • ', 1)[0] + suffix - - -@hook.command("monsterfriends", autohelp=False) -def friends(text, chan, conn, db): - """[{global|average}] - Prints a list of the top monster friends in the channel, if 'global' is specified all channels in the database are included.""" - if chan in opt_out: - return - friends = defaultdict(int) - chancount = defaultdict(int) - if text.lower() == 'global' or text.lower() == 'average': - out = "Monster friend scores across the network: " - scores = db.execute(select([table.c.name, table.c.befriend]) \ - .where(table.c.network == conn.name) \ - .order_by(desc(table.c.befriend))) - if scores: - for row in scores: - if row[1] == 0: - continue - chancount[row[0]] += 1 - friends[row[0]] += row[1] - if text.lower() == 'average': - for k, v in friends.items(): - friends[k] = int(v / chancount[k]) - else: - return "it appears no one has friended any monsters yet." - else: - out = "Monster friend scores in {}: ".format(chan) - scores = db.execute(select([table.c.name, table.c.befriend]) \ - .where(table.c.network == conn.name) \ - .where(table.c.chan == chan.lower()) \ - .order_by(desc(table.c.befriend))) - if scores: - for row in scores: - if row[1] == 0: - continue - friends[row[0]] += row[1] - else: - return "it appears no one has friended any monsters yet." - - topfriends = sorted(friends.items(), key=operator.itemgetter(1), reverse=True) - url = web.paste(json.dumps(topfriends, indent=4)) - out += ' • '.join(["{}: {}".format('\x02' + k[:1] + u'\u200b' + k[1:] + '\x02', str(v)) for k, v in topfriends]) - out = smart_truncate(out) - out += " " + url - return out - - -@hook.command("monsterkillers", autohelp=False) -def killers(text, chan, conn, db): - """[{global|average}] - Prints a list of the top monster killers in the channel, if 'global' is specified all channels in the database are included.""" - if chan in opt_out: - return - killers = defaultdict(int) - chancount = defaultdict(int) - if text.lower() == 'global' or text.lower() == 'average': - out = "Monster killer scores across the network: " - scores = db.execute(select([table.c.name, table.c.shot]) \ - .where(table.c.network == conn.name) \ - .order_by(desc(table.c.shot))) - if scores: - for row in scores: - if row[1] == 0: - continue - chancount[row[0]] += 1 - killers[row[0]] += row[1] - if text.lower() == 'average': - for k, v in killers.items(): - killers[k] = int(v / chancount[k]) - else: - return "it appears no one has killed any monsters yet." - else: - out = "Monster killer scores in {}: ".format(chan) - scores = db.execute(select([table.c.name, table.c.shot]) \ - .where(table.c.network == conn.name) \ - .where(table.c.chan == chan.lower()) \ - .order_by(desc(table.c.shot))) - if scores: - for row in scores: - if row[1] == 0: - continue - killers[row[0]] += row[1] - else: - return "it appears no on has killed any monsters yet." - - topkillers = sorted(killers.items(), key=operator.itemgetter(1), reverse=True) - url = web.paste(json.dumps(topkillers, indent=4)) - out += ' • '.join(["{}: {}".format('\x02' + k[:1] + u'\u200b' + k[1:] + '\x02', str(v)) for k, v in topkillers]) - out = smart_truncate(out) + " " + url - return out - - -# @hook.command("monsterforgive", permissions=["op", "ignore"]) -def duckforgive(text): - """Allows people to be removed from the mandatory cooldown period.""" - global scripters - if text.lower() in scripters and scripters[text.lower()] > time(): - scripters[text.lower()] = 0 - return "{} has been removed from the mandatory cooldown period.".format(text) - else: - return "I couldn't find anyone banned from the hunt by that nick" - - -# @hook.command("hunt_opt_out", permissions=["op", "ignore"], autohelp=False) -def hunt_opt_out(text, chan, db, conn): - """Running this command without any arguments displays the status of the current channel. hunt_opt_out add #channel will disable all duck and monster hunt commands in the specified channel. hunt_opt_out remove #channel will re-enable the game for the specified channel.""" - if not text: - if chan in opt_out: - return "Monster hunt is disabled in {}. To re-enable it run .hunt_opt_out remove #channel".format(chan) - else: - return "Monster hunt is enabled in {}. To disable it run .hunt_opt_out add #channel".format(chan) - if text == "list": - return ", ".join(opt_out) - if len(text.split(' ')) < 2: - return "please specify add or remove and a valid channel name" - command = text.split()[0] - channel = text.split()[1] - if not channel.startswith('#'): - return "Please specify a valid channel." - if command.lower() == "add": - if channel in opt_out: - return "Monster hunt has already been disabled in {}.".format(channel) - query = optout.insert().values( - network=conn.name, - chan=channel.lower()) - db.execute(query) - db.commit() - load_optout(db) - return "The monster hunt has been successfully disabled in {}.".format(channel) - if command.lower() == "remove": - if not channel in opt_out: - return "Monster hunt is already enabled in {}.".format(channel) - delete = optout.delete(optout.c.chan == channel.lower()) - db.execute(delete) - db.commit() - load_optout(db) - - -# @hook.command("monstermerge", permissions=["botcontrol"]) -def duck_merge(text, conn, db, message): - """Moves the monster scores from one nick to another nick. Accepts two nicks as input the first will have their monster scores removed the second will have the first score added. Warning this cannot be undone.""" - oldnick, newnick = text.lower().split() - if oldnick == newnick: - return "please specify two different nicks." - if not oldnick or not newnick: - return "Please specify two nicks for this command." - oldnickscore = db.execute(select([table.c.name, table.c.chan, table.c.shot, table.c.befriend]) - .where(table.c.network == conn.name) - .where(table.c.name == oldnick)).fetchall() - newnickscore = db.execute(select([table.c.name, table.c.chan, table.c.shot, table.c.befriend]) - .where(table.c.network == conn.name) - .where(table.c.name == newnick)).fetchall() - duckmerge = defaultdict(lambda: defaultdict(int)) - duckmerge["TKILLS"] = 0 - duckmerge["TFRIENDS"] = 0 - channelkey = {"update": [], "insert": []} - if oldnickscore: - if newnickscore: - for row in newnickscore: - duckmerge[row["chan"]]["shot"] = row["shot"] - duckmerge[row["chan"]]["befriend"] = row["befriend"] - for row in oldnickscore: - if row["chan"] in duckmerge: - duckmerge[row["chan"]]["shot"] = duckmerge[row["chan"]]["shot"] + row["shot"] - duckmerge[row["chan"]]["befriend"] = duckmerge[row["chan"]]["befriend"] + row["befriend"] - channelkey["update"].append(row["chan"]) - duckmerge["TKILLS"] = duckmerge["TKILLS"] + row["shot"] - duckmerge["TFRIENDS"] = duckmerge["TFRIENDS"] + row["befriend"] - else: - duckmerge[row["chan"]]["shot"] = row["shot"] - duckmerge[row["chan"]]["befriend"] = row["befriend"] - channelkey["insert"].append(row["chan"]) - duckmerge["TKILLS"] = duckmerge["TKILLS"] + row["shot"] - duckmerge["TFRIENDS"] = duckmerge["TFRIENDS"] + row["befriend"] - else: - for row in oldnickscore: - duckmerge[row["chan"]]["shot"] = row["shot"] - duckmerge[row["chan"]]["befriend"] = row["befriend"] - channelkey["insert"].append(row["chan"]) - # TODO: Call dbupdate() and db_add_entry for the items in duckmerge - for channel in channelkey["insert"]: - dbadd_entry(newnick, channel, db, conn, duckmerge[channel]["shot"], duckmerge[channel]["befriend"]) - for channel in channelkey["update"]: - dbupdate(newnick, channel, db, conn, duckmerge[channel]["shot"], duckmerge[channel]["befriend"]) - query = table.delete() \ - .where(table.c.network == conn.name) \ - .where(table.c.name == oldnick) - db.execute(query) - db.commit() - message("Migrated {} monster kills and {} monster friends from {} to {}".format(duckmerge["TKILLS"], - duckmerge["TFRIENDS"], oldnick, - newnick)) - else: - return "There are no monster scores to migrate from {}".format(oldnick) - - -@hook.command("monsters", autohelp=False) -def ducks_user(text, nick, chan, conn, db, message): - """ - Prints a users monster stats. If no nick is input it will check the calling username.""" - name = nick.lower() - if text: - name = text.split()[0].lower() - ducks = defaultdict(int) - scores = db.execute(select([table.c.name, table.c.chan, table.c.shot, table.c.befriend]) - .where(table.c.network == conn.name) - .where(table.c.name == name)).fetchall() - if scores: - for row in scores: - if row["chan"].lower() == chan.lower(): - ducks["chankilled"] += row["shot"] - ducks["chanfriends"] += row["befriend"] - ducks["killed"] += row["shot"] - ducks["friend"] += row["befriend"] - ducks["chans"] += 1 - if ducks["chans"] == 1: - message("{} has killed {} and befriended {} monsters in {}.".format(name, ducks["chankilled"], - ducks["chanfriends"], chan)) - return - kill_average = int(ducks["killed"] / ducks["chans"]) - friend_average = int(ducks["friend"] / ducks["chans"]) - message( - "\x02{}'s\x02 duck stats: \x02{}\x02 killed and \x02{}\x02 befriended in {}. Across {} channels: \x02{}\x02 killed and \x02{}\x02 befriended. Averaging \x02{}\x02 kills and \x02{}\x02 friends per channel.".format( - name, ducks["chankilled"], ducks["chanfriends"], chan, ducks["chans"], ducks["killed"], ducks["friend"], - kill_average, friend_average)) - else: - return "It appears {} has not participated in the monster hunt.".format(name) - - -@hook.command("monsterstats", autohelp=False) -def duck_stats(chan, conn, db, message): - """- Prints monster statistics for the entire channel and totals for the network.""" - ducks = defaultdict(int) - scores = db.execute(select([table.c.name, table.c.chan, table.c.shot, table.c.befriend]) - .where(table.c.network == conn.name)).fetchall() - if scores: - ducks["friendchan"] = defaultdict(int) - ducks["killchan"] = defaultdict(int) - for row in scores: - ducks["friendchan"][row["chan"]] += row["befriend"] - ducks["killchan"][row["chan"]] += row["shot"] - # ducks["chans"] += 1 - if row["chan"].lower() == chan.lower(): - ducks["chankilled"] += row["shot"] - ducks["chanfriends"] += row["befriend"] - ducks["killed"] += row["shot"] - ducks["friend"] += row["befriend"] - ducks["chans"] = int((len(ducks["friendchan"]) + len(ducks["killchan"])) / 2) - killerchan, killscore = sorted(ducks["killchan"].items(), key=operator.itemgetter(1), reverse=True)[0] - friendchan, friendscore = sorted(ducks["friendchan"].items(), key=operator.itemgetter(1), reverse=True)[0] - message( - "\x02Monster Stats:\x02 {} killed and {} befriended in \x02{}\x02. Across {} channels \x02{}\x02 monsters have been killed and \x02{}\x02 befriended. \x02Top Channels:\x02 \x02{}\x02 with {} kills and \x02{}\x02 with {} friends".format( - ducks["chankilled"], ducks["chanfriends"], chan, ducks["chans"], ducks["killed"], ducks["friend"], - killerchan, killscore, friendchan, friendscore)) - else: - return "It looks like there has been no monster activity on this channel or network."