267 lines
10 KiB
Python
267 lines
10 KiB
Python
import plugin, badge, random, json, traceback, requests, io, importlib
|
|
from pluralslib import plural, are
|
|
from bot import IRCLine
|
|
from collections import Counter
|
|
importlib.reload(badge)
|
|
BOT = None
|
|
|
|
def pack_file(txt):
|
|
f = io.StringIO()
|
|
f.write(txt)
|
|
f.seek(0)
|
|
return {"file": ("tmp.txt",f,"text/plain")}
|
|
|
|
class BadgePopData(plugin.Data):
|
|
def serialize(self):
|
|
return json.dumps(self.value.to_json())
|
|
def deserialize(self,s):
|
|
self.value = badge.BadgePopulation.from_json(s)
|
|
|
|
class ListData(plugin.DictData):
|
|
def __init__(self,filename):
|
|
super().__init__(filename)
|
|
self.value = []
|
|
self.load()
|
|
def add(self,i):
|
|
self.value.append(i)
|
|
self.save()
|
|
def remove(self,i):
|
|
self.value.remove(i)
|
|
self.save()
|
|
def __contains__(self,i): return i in self.value
|
|
|
|
population = BadgePopData(badge.BadgePopulation())
|
|
population.load("badges.json")
|
|
|
|
timeouts = plugin.DictData("timeouts.json")
|
|
optouts = ListData("optouts.json")
|
|
|
|
badge_weights = {'Berrybadge': 0.65, 'Rockbadge': 0.1, 'Waterbadge': 0.05, 'Firebadge': 0.15, 'Tildebadge': 0.001, 'Shadybadge': 0.02, 'Musicbadge': 0.019, 'Sportsbadge': 0.01}
|
|
|
|
def privmsg(target,message):
|
|
return IRCLine("PRIVMSG",target,":"+message).line
|
|
|
|
def respond(event,message):
|
|
if BOT is None: return
|
|
BOT.socket.send(privmsg(event.target if event.target.startswith("#") else event.hostmask.nick,(event.hostmask.nick+": " if event.target.startswith("#") else "")+message))
|
|
|
|
def on_privmsg(event):
|
|
if BOT is None: return
|
|
account = event.tags.get("account",None)
|
|
if account is None: return
|
|
if not event.target.startswith("#"): return
|
|
if timeouts.get(event.target,0)==0 and account not in optouts:
|
|
badge_to_give = random.choices(list(badge_weights.keys()),list(badge_weights.values()))[0]
|
|
if account not in population.value.badges:
|
|
BOT.socket.send(privmsg(event.hostmask.nick,f"Hey, you've got a badge! Badges are a nice way to show how active you are in channels like {event.target}. To see what you can do with these badges, respond 'help' to this message."))
|
|
BOT.socket.send(privmsg(event.hostmask.nick,"This will be the only time I contact you, unless you use commands here."))
|
|
BOT.socket.send(privmsg(event.hostmask.nick,f"Any complaints should be directed at khuxkm, and you can opt out from badges and future use of this bot with the command '{BOT.prefix}optout'."))
|
|
population.value.give_badge(account,badge_to_give)
|
|
population.save("badges.json")
|
|
timeouts[event.target]=random.randint(10,30)
|
|
elif timeouts.get(event.target,0)>0:
|
|
timeouts[event.target]-=1
|
|
if event.message=="!botlist" or event.message=="!rollcall":
|
|
respond(event,"Hi! I'm the badger! I give out badges randomly. "+("Commands you can use include 'listbadges', 'transmute', and 'badgeinfo'." if account is not None else "To get started, log in to a services account! (/msg NickServ help)")+" Source: https://ttm.sh/Eyx")
|
|
|
|
def on_cmd_help(event):
|
|
if BOT is None: return
|
|
account = event.tags.get("account",None)
|
|
if len(event.parts)==0:
|
|
respond(event,"Hi! I'm the badger! I give out badges randomly. "+("Commands you can use include 'listbadges', 'transmute', and 'badgeinfo'. Use 'help <command>' for more help." if account is not None else "To get started, log in to a services account! (/msg NickServ help)"))
|
|
return None
|
|
if account is None: return
|
|
if event.parts[0]=="listbadges":
|
|
respond(event,"Lists the badges in your possession. Usage: listbadges")
|
|
elif event.parts[0]=="transmute":
|
|
respond(event,"Transmutes 3 or more badges into one, possibly rarer, badge. Usage: transmute <badge one> <badge two> <badge three> [badge four...]")
|
|
elif event.parts[0]=="badgeinfo":
|
|
respond(event,"Gives info on the rarity of a badge. Usage: badgeinfo all OR badgeinfo <badge one> [<badge two> <badge three>...]")
|
|
|
|
def on_cmd_listbadges(event):
|
|
if BOT is None: return
|
|
account = event.tags.get("account",None)
|
|
if account is None: return
|
|
counts = Counter([x.name for x in population.value.badges.get(account,"")])
|
|
ret = []
|
|
for item in sorted(list(counts.items()),key=lambda x: -badge_weights[x[0]]):
|
|
ret.append("{} (x{!s})".format(*item))
|
|
if len(counts.items())==0:
|
|
respond(event,"You don't have any badges yet! Just stay active in the channel and you'll get one eventually.")
|
|
else:
|
|
respond(event,"You have: "+", ".join(ret))
|
|
|
|
class AmbiguousBadge(Exception):
|
|
pass
|
|
|
|
class NoSuchBadge(Exception):
|
|
pass
|
|
|
|
def resolve_badge(s,names=None):
|
|
if names is None: names = list(badge_weights.keys())
|
|
if len(s)==0: raise NoSuchBadge("No such badge `` (check for extra spaces in your command)")
|
|
elif len(s)==1: s=s.upper()
|
|
else: s=s[0].upper()+s[1:].lower() # len(s)>1
|
|
ret = [name for name in names if name.startswith(s)]
|
|
if len(ret)==0: raise NoSuchBadge(f"No such badge `{s}`.")
|
|
elif len(ret)>1:
|
|
r = ", or ".join(ret)
|
|
raise AmbiguousBadge(f"Ambiguous badge choice `{s}` (do you mean {r}?)")
|
|
else: # len(ret)==1
|
|
return ret[0]
|
|
|
|
def on_cmd_transmute(event,silent=False):
|
|
if silent: respond=lambda *x: None
|
|
else: respond=globals()["respond"]
|
|
if BOT is None: return
|
|
account = event.tags.get("account",None)
|
|
if account is None: return
|
|
parts = event.parts
|
|
# TODO: implement auto transmute of 3 worst badges
|
|
if len(parts)==1 and parts[0].lower() in 'a auto'.split():
|
|
rarities = badge.calculate_rarities(population.value.population)
|
|
badges = list(reversed(sorted(population.value.badges.get(account,[]),key=lambda x: rarities[x.name][2])))
|
|
parts = [x.name for x in badges[:3]]
|
|
if "Tildebadge" in parts:
|
|
respond(event,"This would burn a Tildebadge. To proceed, type `+transmute {}` (or equivalent)".format(" ".join(parts)))
|
|
return
|
|
try:
|
|
badges_transmutable = [resolve_badge(x) for x in parts]
|
|
except NoSuchBadge as e:
|
|
respond(event,e.args[0])
|
|
return
|
|
except AmbiguousBadge as e:
|
|
respond(event,e.args[0])
|
|
return
|
|
except:
|
|
traceback.print_exc()
|
|
url = "Error saving traceback! Tell khuxkm to check error.log!"
|
|
err = traceback.format_exc()
|
|
try:
|
|
with open("error.log","a") as f:
|
|
f.write(err+"\n"+("-"*80)+"\n")
|
|
r = requests.post("https://ttm.sh",files=pack_file(err))
|
|
url = r.text.strip()
|
|
except: pass
|
|
respond(event,"Something went wrong! Error: "+url)
|
|
respond(event,"If you lost badges because of this error, please tell khuxkm which badges and he will refund you. (You shouldn't have in this case.)")
|
|
return
|
|
print(badges_transmutable)
|
|
if len(badges_transmutable)<3:
|
|
respond(event,"You must insert at least 3 badges for use in transmutation.")
|
|
return
|
|
try:
|
|
badge_result = population.value.transmute(account,*badges_transmutable)
|
|
except badge.UserDoesntHaveEnoughBadges:
|
|
respond(event,"You must have at least one (1) of each badge you wish to use in the transmutation.")
|
|
return
|
|
except:
|
|
url = "Error saving traceback! Tell khuxkm to check error.log!"
|
|
err = traceback.format_exc()
|
|
try:
|
|
with open("error.log","a") as f:
|
|
f.write(err+"\n"+("-"*80)+"\n")
|
|
r = requests.post("https://ttm.sh",files=pack_file(err))
|
|
url = r.text.strip()
|
|
except: pass
|
|
respond(event,"Something went wrong! Error: "+url)
|
|
respond(event,"If you lost badges because of this error, please tell khuxkm which badges and he will refund you.")
|
|
return
|
|
respond(event,"You put in the {!s} badges above, and out pops a {}!".format(len(parts),badge_result))
|
|
population.value.give_badge(account,badge_result,False)
|
|
population.save("badges.json")
|
|
|
|
def on_cmd_badgeinfo(event):
|
|
bag = population.value.population
|
|
if "list" in event.parts:
|
|
respond(event,"The badges are: "+",".join(sorted(list(set(badge.name for badge in bag)),key=lambda x: -badge_weights[x])))
|
|
if "all" in event.parts:
|
|
event.data["parts"]=sorted(list(set(badge.name for badge in bag)),key=lambda x: -badge_weights[x])
|
|
for badge in event.parts:
|
|
badge = badge[0].upper()+badge[1:].lower()
|
|
b = [obadge for obadge in bag if obadge.name==badge]
|
|
count = len(b)
|
|
if count==0: continue
|
|
normal = len([x for x in b if x.normal])
|
|
rarity = population.value.rarity(badge)
|
|
respond(event,"There {} {} in existence, {!s} of them randomly generated (effective rarity of {:0.2%})".format(are(count),plural(count,badge),normal,rarity))
|
|
|
|
def on_cmd_optout(event):
|
|
if not BOT: return
|
|
account = event.tags.get("account")
|
|
if not account: return
|
|
if account in optouts:
|
|
respond(event,"You're already opted out.")
|
|
return
|
|
optouts.add(account)
|
|
respond(event,"Fine. I won't *badger* you anymore.")
|
|
|
|
def on_cmd_optin(event):
|
|
if not BOT: return
|
|
account = event.tags.get("account")
|
|
if not account: return
|
|
if account not in optouts:
|
|
respond(event,"You're already opted in.")
|
|
return
|
|
optouts.remove(account)
|
|
respond(event,"Alright! Let's play!")
|
|
|
|
def on_cmd_transmuteall(event):
|
|
if BOT is None: return
|
|
account = event.tags.get("account",None)
|
|
if account is None: return
|
|
while True:
|
|
rarities = badge.calculate_rarities(population.value.population)
|
|
badges = list(reversed(sorted(population.value.badges.get(account,[]),key=lambda x: rarities[x.name][2])))
|
|
parts = [x.name for x in badges[:3]]
|
|
if "Tildebadge" in parts or len(parts)<3:
|
|
respond(event,"Finished!")
|
|
return
|
|
event.data["parts"]=parts
|
|
on_cmd_transmute(event,True)
|
|
|
|
def admin_givebadge(event):
|
|
print(event.name,event.data)
|
|
try:
|
|
account, badge = event.parts[:2]
|
|
badge = badge[0].upper()+badge[1:].lower()
|
|
normal=True
|
|
if len(event.parts)==3 and event.parts[2].lower() in ("n","no","f","false"): normal=False
|
|
population.value.give_badge(account,badge,normal)
|
|
population.save("badges.json")
|
|
except: pass
|
|
|
|
def admin_takebadge(event):
|
|
print(event.name,event.data)
|
|
try:
|
|
account, badge = event.parts[:2]
|
|
badge = badge[0].upper()+badge[1:].lower()
|
|
i=len(population.value.badges.get(account,[]))-1
|
|
while i>0:
|
|
if populations.value.badges[account][i].name==badge:
|
|
populations.value.badges[account].pop(i)
|
|
i=-1
|
|
population.save("badges.json")
|
|
except: pass
|
|
|
|
def admin_manualoptout(event):
|
|
for account in event.parts:
|
|
if account not in optouts:
|
|
optouts.add(account)
|
|
optouts.value = list(set(optouts.value))
|
|
optouts.save()
|
|
|
|
def register(bot):
|
|
global BOT
|
|
BOT=bot
|
|
bot.event_manager.on("privmsg",on_privmsg)
|
|
bot.event_manager.on("command_help",on_cmd_help)
|
|
bot.event_manager.on("command_listbadges",on_cmd_listbadges)
|
|
bot.event_manager.on("command_transmute",on_cmd_transmute)
|
|
bot.event_manager.on("command_transmuteall",on_cmd_transmuteall)
|
|
bot.event_manager.on("command_badgeinfo",on_cmd_badgeinfo)
|
|
bot.event_manager.on("command_optout",on_cmd_optout)
|
|
bot.event_manager.on("command_optin",on_cmd_optin)
|
|
bot.event_manager.on("admin_givebadge",admin_givebadge)
|
|
bot.event_manager.on("admin_optout",admin_manualoptout)
|