def __init__(self, server): self.server = server self.yodaemon = YoCollector(server, self.yo_receive) self.yodaemon.start() self.routesf = server.get_config_dir("yo_routes.json") self.routes = Config(self.routesf, default={}) self.usersf = server.get_config_dir("yo_users.json") self.users = Config(self.usersf, default={}) self.keysf = server.get_config_dir("yo_keys.json") self.keys = Config(self.keysf, default={}) super().__init__(server)
def __init__(self, server): self.server = server self.configf = server.get_config_dir("pushbullet.json") self.config = Config(self.configf, default={"accounts":{}, "users":{}}) self.usersettingsf = server.get_config_dir("pushbullet_settings.json") self.usersettings = Config(self.usersettingsf, default={}) self.bouncefmt = "\x0303· \x02%(nick)s\x0f\x0f: %(body)s" self.listeners = [] self.skip = set() self.sent = set() self.pushlock = threading.Lock() self.watchers = {} self.channels = {} self.active = {} self.rejoin_ignore = {} self.lower = server.lower self.listen() for channel, account in self.config["accounts"].items(): try: self.channels[channel] = requests.get("https://api.pushbullet.com/v2/users/me", headers={"Authorization": "Bearer " + account["token"]}).json() except: pass super().__init__(server)
def __init__(self, server): self.instances.append(self) self.games = {} self.lock = threading.Lock() self.config = Config(server.get_config_dir(CONFIG_FILE)) self.expansiondir = server.get_config_dir("CardsAgainstHumanity") if not os.path.exists(self.expansiondir): os.makedirs(self.expansiondir, exist_ok=True) if not os.path.exists(self.expansiondir + "/statistics.db"): # Initialise the db with sqlite3.connect(self.expansiondir + "/statistics.db") as db: db.execute( "CREATE TABLE white (timestamp int, nick text, channel text, round int, czar text, prompt text, cards text, bet int, rank int);" ) CardsAgainstHumanity.loadcards(self.expansiondir) server.register("privmsg", self.trigger) server.register("privmsg", self.custom_cards) server.register("privmsg", self.remove_player)
class PushBullet(Callback): HLCMD = r"\.highlight(?: (always|(?:\S+(?:,\s*\S+)* )?inactive(?: \d+)?|(?:\S+(?:,\s*\S+)* )?offline|remove))?(?: (.+))?" def __init__(self, server): self.server = server self.configf = server.get_config_dir("pushbullet.json") self.config = Config(self.configf, default={"accounts":{}, "users":{}}) self.usersettingsf = server.get_config_dir("pushbullet_settings.json") self.usersettings = Config(self.usersettingsf, default={}) self.bouncefmt = "\x0303· \x02%(nick)s\x0f\x0f: %(body)s" self.listeners = [] self.skip = set() self.sent = set() self.pushlock = threading.Lock() self.watchers = {} self.channels = {} self.active = {} self.rejoin_ignore = {} self.lower = server.lower self.listen() for channel, account in self.config["accounts"].items(): try: self.channels[channel] = requests.get("https://api.pushbullet.com/v2/users/me", headers={"Authorization": "Bearer " + account["token"]}).json() except: pass super().__init__(server) def listen(self): for channel, account in self.config["accounts"].items(): self.listeners.append(PushListener(account["token"], partial(self.update, channel))) for listener in self.listeners: listener.start() def update(self, account): acc = self.config["accounts"][account] params = {"modified_after": acc["last"]} headers = {"Authorization": "Bearer " + acc["token"]} watchers = self.watchers.setdefault(self.lower(account), set()) req = requests.get("https://api.pushbullet.com/v2/pushes", params=params, headers=headers) pushes = req.json()["pushes"] if not pushes: return pushes.reverse() for push in pushes: sender_email = push["sender_email"].lower() hlmatch = re.match(self.HLCMD, push.get("body", "")) with self.pushlock: if push["iden"] in self.skip: self.skip.remove(push["iden"]) elif hlmatch: when, word = hlmatch.groups() if sender_email in self.config["users"]: nick = self.config["users"][sender_email] settings = self.usersettings.get(self.lower(account), []) if word is None: word = nick if when is None: when = "offline" pattern = [word, sender_email, when] matches = [i for i, x in enumerate(settings) if x[0:2] == pattern[0:2]] if when == "remove": settings = [i for i in settings if i[0:2] != pattern[0:2]] hlconfirm = {"type": "note", "email": sender_email, "title": "* %r removed from alerts." % word} elif matches and pattern in settings: settings.remove(pattern) hlconfirm = {"type": "note", "email": sender_email, "title": "* %r removed from alerts." % word} elif matches: for i in matches: settings[i] = pattern hlconfirm = {"type": "note", "email": sender_email, "title": "* Alert for %r changed to %r." % (word, when)} else: settings.append(pattern) hlconfirm = {"type": "note", "email": sender_email, "title": "* %r added to alerts." % word} self.skip.add(self.push(hlconfirm, acc["token"])) self.usersettings[self.lower(account)] = settings # Handle user joins elif push.get("body", "") == ".join": if sender_email in self.config["users"]: nick = self.config["users"][sender_email] self.server.message("03│ ⁍ │ %s has joined the conversation via pushbullet." % nick, account) watchers.add(sender_email) push_join = {"type": "note", "title": "* %s has joined via PushBullet" % nick} actives = {"type": "note", "email": push["sender_email"], "title": "* Now listening to %s" % account} ausers = [k for k, v in self.active.setdefault(self.lower(account), {}).items() if time.time() - v < ACTIVITY_TIMEOUT] if ausers: actives["body"] = "Active users:\n%s" % (", ".join(ausers)) for email in watchers: if email.lower() != sender_email: push_join["email"] = email self.skip.add(self.push(push_join, acc["token"])) self.skip.add(self.push(actives, acc["token"])) elif push.get("body", "") == ".part": if sender_email in watchers: nick = self.config["users"][sender_email] self.server.message("03│ ⁍ │ %s has stopped listening to the conversation." % nick, account) watchers.remove(sender_email) push_part = {"type": "note", "title": "* %s is no longer receiving updates via PushBullet" % nick} partconfirm = {"type": "note", "email": push["sender_email"], "title": "* No longer listening to %s" % account} for email in watchers: if email.lower() != sender_email: push_part["email"] = email self.skip.add(self.push(push_part, acc["token"])) self.skip.add(self.push(partconfirm, acc["token"])) else: display_sender = self.config["users"].get(sender_email, push["sender_email"]) for email in watchers: if email.lower() != sender_email: self.skip.add(self.push(push_bounce(push, display_sender, email), acc["token"])) if push["iden"] not in self.sent: @command("reply", r"(?:(https?://\S+|:.+?:))?\s*(.*)") def pushreply(server, message, link, text, push=push): user = push["sender_email"] return self.send_push.funct(self, server, message, user, link, text) self.server.reply_hook = pushreply if sender_email in watchers: self.server.message(self.bouncefmt % {"nick": display_sender, "body": push_text(push)}, account) else: self.server.message(push_format(push, self.sent, self.config["users"]), account) acc["last"] = max(push["modified"], acc["last"]) self.save(account, acc) def save(self, channel, account): with self.config as conf: conf["accounts"][channel] = account def queue(self, push): pass @command("setpush", "(.+@.+)") def set_push(self, server, msg, email): with self.config as conf: conf["users"][email.lower()] = msg.address.nick return "03│ ⁍ │ Associated %s with pushbullet account %s." % (msg.address.nick, email) @command("pushassoc", r"(#\S+)\s+(\S+)", admin=True) def add_channel(self, server, msg, channel, token): account = {"token": token, "last": time.time()} with self.config as conf: conf["accounts"][server.lower(channel)] = account listener = PushListener(account["token"], partial(self.update, channel)) self.listeners.append(listener) listener.start() self.channels[channel] = requests.get("https://api.pushbullet.com/v2/users/me", headers={"Authorization": "Bearer " + token}).json() return "03│ ⁍ │ Done." @command("help", r"(?:(\S+)\s+)?pushbullet") def pushbullet_info(self, server, msg, user): try: acc = self.config["accounts"][self.lower(msg.context)] email = self.channels[self.lower(msg.context)]["email"] except KeyError: return "04│ ⁍ │ This channel has no associated pushbullet." steps = ["Go to https://www.pushbullet.com/add-friend", "Add %s (%s) as a friend" % (msg.context, email), "Visit https://www.pushbullet.com/?email=%s and send your first push to the channel!" % email] if user: user_email = self.get_user(user) if user not in self.config["users"]: steps = ["If you don't have an account: Set up an account at http://www.pushbullet.com/ and install pushbullet on your devices", "Type /msg %s .setpush EMAIL_ADDRESS" % server.nick] + steps else: return "03│ ⁍ │ Type .setpush \x02email\x02, then go to 12https://www.pushbullet.com/add-friend\x0f and add \x0303%s\x03 as a friend." % email if user_email is None: return "03│ ⁍ │ %s: type .setpush \x02email\x02, then go to 12https://www.pushbullet.com/add-friend\x0f and add \x0303%s\x03 as a friend." % (user, email) else: with self.pushlock: self.sent.add(self.push({"type" : "link", "title": "Add %s on PushBullet" % msg.context, "body" : "\r\n".join("%d) %s" % (i+1, s) for i, s in enumerate(steps)), "link" : "https://www.pushbullet.com/add-friend", "email": user_email}, acc["token"])) return "03│ ⁍ │ I've sent instructions to %s's pushbullet address." % user @command("send", r"(\S+(?:,\s*\S+)*)(?:\s+(https?://\S+|:.+?:))?(?:\s+(.+))?") def send_push(self, server, msg, user, link, text): try: acc = self.config["accounts"][server.lower(msg.context)] except KeyError: return push = {} user = self.get_user(user) if user is None: return "03│ ⁍ │ %s has not associated their pushbullet." % user push["title"] = msg.address.nick + ":" if link: if link.startswith(":"): link = image_search(link[1:-1])[0]["url"] push["url"] = link push["type"] = "link" else: push["type"] = "note" if text: push["body"] = text push["email"] = user with self.pushlock: self.sent.add(self.push(push, acc["token"])) @msghandler def update_watchers(self, server, msg): ctx = server.lower(msg.context) # update user = server.lower(msg.address.nick) self.active.setdefault(ctx, {})[user] = time.time() highlighted = [] if ctx not in self.config["accounts"]: return acc = self.config["accounts"][ctx] if ctx in self.watchers: watchers = self.watchers[ctx] push = {"type": "note"} if msg.text.startswith("\x01ACTION ") and msg.text.endswith("\x01"): push["body"] = "* %s %s" % (msg.address.nick, ircstrip(msg.text[8:-1])) else: push["body"], push["title"] = ircstrip(msg.text), msg.address.nick for email in watchers: if any(email == target and hl_match(word, msg.text) for word, target, _ in self.usersettings.get(ctx, [])): hlpush = {"type": "note", "email": email, "body": push["body"]} if "title" in push: hlpush["title"] = "🔔 " + push["title"] else: hlpush["title"] = "🔔 Highlight from " + msg.address.nick with self.pushlock: self.skip.add(self.push(hlpush, acc["token"])) highlighted.append(email) else: push["email"] = email with self.pushlock: self.skip.add(self.push(push, acc["token"])) for word, email, when in self.usersettings.get(ctx, []): nick = server.lower(self.config["users"][email]) # Parse when inactive_match = re.match(r"(\S+(?:,\s*\S+)* )?inactive( \d+)?", when) offline_match = re.match(r"(\S+(?:,\s*\S+)* )?offline", when) if inactive_match: who, timeout = inactive_match.groups() if who is None: who = [nick] else: who = re.split(r",\s*", who.strip()) if timeout is None: timeout = "15" timeout = 60 * int(timeout.strip()) when = "inactive" elif offline_match: who, = offline_match.groups() if who is None: who = [nick] else: who = re.split(r",\s*", who) when = "offline" if email not in highlighted and hl_match(word, msg.text): if (when == "always" or (when == "offline" and server.isIn(ctx, server.channels) and not any(server.isIn(i, server.channels[ctx]) for i in who)) or (when =="inactive" and all(not server.isIn(i, self.active[ctx]) or time.time() - self.active[ctx][self.lower(i)] >= timeout for i in who))): push = {"type": "note", "title": "🔔 Highlight from %s" % msg.address.nick, "body": ircstrip(msg.text), "email":email} if msg.text.startswith("\x01ACTION ") and msg.text.endswith("\x01"): push["body"] = "* %s %s" % (msg.address.nick, ircstrip(msg.text[8:-1])) with self.pushlock: self.skip.add(self.push(push, acc["token"])) highlighted.append(email) ## Channel state tracking def update_active_quit(self, server, line) -> "quit": words = line.split(" ", 2) nick = Address(words[0]).nick lnick = server.lower(nick) requires_updates = [] for channel in self.active: if lnick in self.active[channel]: if time.time() - self.active[channel][lnick] < ACTIVITY_TIMEOUT: requires_updates.append(channel) else: self.rejoin_ignore.setdefault(channel, {})[lnick] = time.time() del self.active[channel][lnick] # Update watchers push = {"type": "note", "title": "* %s has disconnected" % nick, "body": cstrip(words[-1])} for channel in requires_updates: ctx = server.lower(channel) if ctx in self.watchers and ctx in self.config["accounts"]: acc = self.config["accounts"][ctx] watchers = self.watchers[ctx] for email in watchers: push["email"] = email with self.pushlock: self.skip.add(self.push(push, acc["token"])) def update_active_part(self, server, line) -> "part": words = line.split(" ", 3) nick = Address(words[0]).nick lnick = server.lower(nick) channel = server.lower(words[2]) push = {"type": "note", "title": "* %s has left the channel" % nick, "body": cstrip(words[-1])} if lnick in self.active[channel]: if (time.time() - self.active[channel][lnick] < ACTIVITY_TIMEOUT and channel in self.watchers and channel in self.config["accounts"]): # Update watchers acc = self.config["accounts"][channel] watchers = self.watchers[channel] for email in watchers: push["email"] = email with self.pushlock: self.skip.add(self.push(push, acc["token"])) del self.active[channel][lnick] def update_active_nick(self, server, line) -> "nick": words = line.split(" ", 2) nick = Address(words[0]).nick lnick = server.lower(nick) newnick = words[2] requires_updates = [] for channel in self.active: if lnick in self.active[channel]: if time.time() - self.active[channel][lnick] < ACTIVITY_TIMEOUT: requires_updates.append(channel) del self.active[channel][lnick] self.active[channel][server.lower(newnick)] = time.time() # Update watchers push = {"type": "note", "title": "* %s is now known as %s" % (nick, newnick)} for channel in requires_updates: ctx = server.lower(channel) if ctx in self.watchers and ctx in self.config["accounts"]: acc = self.config["accounts"][ctx] watchers = self.watchers[ctx] for email in watchers: push["email"] = email with self.pushlock: self.skip.add(self.push(push, acc["token"])) def update_active_join(self, server, line) -> "join": words = line.split(" ", 3) nick = Address(words[0]).nick lnick = server.lower(nick) channel = server.lower(cstrip(words[2])) push = {"type": "note", "title": "* %s has joined the channel" % nick} if ((lnick not in self.rejoin_ignore.setdefault(channel, {}) or (time.time() - self.rejoin_ignore[channel][lnick]) > ACTIVITY_TIMEOUT) and channel in self.watchers and channel in self.config["accounts"]): # Update watchers acc = self.config["accounts"][channel] watchers = self.watchers[channel] for email in watchers: push["email"] = email with self.pushlock: self.skip.add(self.push(push, acc["token"])) if lnick in self.rejoin_ignore[channel]: del self.rejoin_ignore[channel][lnick] self.active.setdefault(channel, {})[lnick] = time.time() def update_active_kick(self, server, line) -> "kick": words = line.split(" ", 4) kicker = Address(words[0]).nick nick = words[3] lnick = server.lower(nick) channel = server.lower(words[2]) push = {"type": "note", "title": "* %s has kicked %s from %s" % (kicker, nick, words[2]), "body": cstrip(words[-1])} if (channel in self.watchers and channel in self.config["accounts"]): # Update watchers acc = self.config["accounts"][channel] watchers = self.watchers[channel] for email in watchers: push["email"] = email with self.pushlock: self.skip.add(self.push(push, acc["token"])) if lnick in self.active[channel]: del self.active[channel][lnick] def push(self, push, token): headers = {"Authorization": "Bearer " + token} response = requests.post("https://api.pushbullet.com/v2/pushes", headers=headers, data=push).json() return response["iden"] def get_user(self, user): if "@" not in user: users = self.config["users"] users = [i for i in users if self.lower(user) == self.lower(users[i])] if not users: return else: user = users[0] return user def __destroy__(self, server): for listener in self.listeners: listener.listening = False @command("pbflush", admin=True) def pbflush(self, server, msg): for channel, account in self.config["accounts"].items(): self.update(channel) return "03│ ⁍ │ Manually tickled all pushbullet connections." @command("pbrestart", admin=True) def pbrestart(self, server, msg): for listener in self.listeners: listener.listening = False self.listeners = [] self.listen() return "03│ ⁍ │ Restarted all tickle threads."