def new_privmsg(target, text): if irc.client.is_channel(target): username = config["username"] text = utils.trim_length(text, do_warn=True) chatlog.log_chat(irc.client.Event("pubmsg", username, target, [text]), SELF_METADATA) original_privmsg(target, text) elif self.whisperconn: self.whisperconn.whisper(target, text) else: log.info("Not sending private message to %s: %s", target, text)
async def live(lrrbot, conn, event, respond_to): """ Command: !live Post the currenly live fanstreamers. """ try: await extract_new_channels(lrrbot.loop) except urllib.error.HTTPError: pass streams = await twitch.get_streams_followed() if streams == []: return conn.privmsg(respond_to, "No fanstreamers currently live.") streams.sort(key=lambda e: e["channel"]["display_name"]) tag = "Currently live fanstreamers: " # Full message message = tag + ", ".join([ "%s (%s%s)%s%s" % ( data["channel"]["display_name"], data["channel"]["url"], space.SPACE, " is playing %s" % data["game"] if data.get("game") is not None else "", " (%s)" % data["channel"]["status"] if data["channel"].get("status") not in [None, ""] else "" ) for data in streams ]) if utils.check_length(message): return conn.privmsg(respond_to, message) # Shorter message message = tag + ", ".join([ "%s (%s%s)%s" % ( data["channel"]["display_name"], data["channel"]["url"], space.SPACE, " is playing %s" % data["game"] if data.get("game") is not None else "", ) for data in streams ]) if utils.check_length(message): return conn.privmsg(respond_to, message) # Shortest message message = tag + ", ".join([ "%s (%s%s)" % ( data["channel"]["display_name"], data["channel"]["url"], space.SPACE ) for data in streams ]) return conn.privmsg(respond_to, utils.trim_length(message))
def new_privmsg(target, text): if irc.client.is_channel(target): username = config["username"] text = utils.trim_length(text, do_warn=True) chatlog.log_chat( irc.client.Event("pubmsg", username, target, [text]), SELF_METADATA) original_privmsg(target, text) elif self.whisperconn: self.whisperconn.whisper(target, text) else: log.info("Not sending private message to %s: %s", target, text)
async def report_poll(conn, poll_id, respond_to, tag): url = "https://www.strawpoll.me/api/v2/polls/%s" % poll_id data = json.loads(await common.http.request_coro(url)) options = sorted(zip(data["options"], data["votes"]), key=lambda e: (e[1], random.random()), reverse=True) options = "; ".join(map(strawpoll_format, enumerate(options))) response = "Poll complete: %s: %s" % (html.unescape(data["title"]), options) response = utils.trim_length(response) conn.privmsg(respond_to, response) if tag is not None: data['tag'] = tag await common.rpc.eventserver.event('strawpoll-complete', data)
async def live(lrrbot, conn, event, respond_to): """ Command: !live Post the currenly live fanstreamers. """ try: await extract_new_channels(lrrbot.loop) except urllib.error.HTTPError: pass streams = await twitch.get_streams_followed() if streams == []: return conn.privmsg(respond_to, "No fanstreamers currently live.") streams.sort(key=lambda e: e["channel"]["display_name"]) tag = "Currently live fanstreamers: " # Full message message = tag + ", ".join([ "%s (%s%s)%s%s" % (data["channel"]["display_name"], data["channel"]["url"], space.SPACE, " is playing %s" % data["game"] if data.get("game") is not None else "", " (%s)" % data["channel"]["status"] if data["channel"].get("status") not in [None, ""] else "") for data in streams ]) if utils.check_length(message): return conn.privmsg(respond_to, message) # Shorter message message = tag + ", ".join([ "%s (%s%s)%s" % ( data["channel"]["display_name"], data["channel"]["url"], space.SPACE, " is playing %s" % data["game"] if data.get("game") is not None else "", ) for data in streams ]) if utils.check_length(message): return conn.privmsg(respond_to, message) # Shortest message message = tag + ", ".join([ "%s (%s%s)" % (data["channel"]["display_name"], data["channel"]["url"], space.SPACE) for data in streams ]) return conn.privmsg(respond_to, utils.trim_length(message))
def polls(lrrbot, conn, event, respond_to): """ Command: !polls Section: misc List all currently active polls. """ if not lrrbot.polls: return conn.privmsg(respond_to, "No active polls.") now = time.time() messages = [] for end, title, poll_id, respond_to, tag in lrrbot.polls: messages += ["%s (https://www.strawpoll.me/%s%s): %s from now" % (title, poll_id, space.SPACE, common.time.nice_duration(end - now, 1))] conn.privmsg(respond_to, utils.trim_length("Active polls: "+"; ".join(messages)))
def process_card(card, expansion, houses, include_reminder=False): # sanitise card name filtered = clean_text(card.get('internalname', card["card_title"])) if not re_check.match(filtered): print("Still some junk left in name %s (%s)" % (card.get( 'internalname', card["card_title"]), json.dumps(filtered))) print(json.dumps(card)) sys.exit(1) def build_description(): yield card['card_title'] yield ' [' if card.get('is_anomaly'): yield "Anomaly" elif card['house'] in houses: yield houses[card['house']]['name'] else: yield card['house'] if card.get('amber'): yield '\u2014' yield str(card['amber']) yield '<A>' yield '] | ' yield card['card_type'] if card.get('traits'): yield ' \u2014 ' yield card['traits'] if card['card_type'] == "Creature": yield ' [' yield str(card['power']) if card.get('armor'): yield '/' yield str(card['armor']) yield ']' if card.get('card_text'): yield ' | ' yield process_text(card['card_text'], include_reminder) desc = ''.join(build_description()) desc = re_multiplespaces.sub(' ', desc).strip() desc = utils.trim_length(desc) numbers = card['card_number'] if card.get('card_number') else [] if not isinstance(numbers, list): numbers = [numbers] numbers = [(expansion['code'].lower(), str(i)) for i in numbers] hidden = 'internalname' in card return filtered, card['card_title'], desc, numbers, hidden
async def report_poll(conn, poll_id, respond_to, tag): url = "https://www.strawpoll.me/api/v2/polls/%s" % poll_id data = json.loads(await common.http.request_coro(url)) options = sorted(zip(data["options"], data["votes"]), key=lambda e: (e[1], random.random()), reverse=True) options = "; ".join(map(strawpoll_format, enumerate(options))) response = "Poll complete: %s: %s" % (html.unescape( data["title"]), options) response = utils.trim_length(response) conn.privmsg(respond_to, response) if tag is not None: data['tag'] = tag await common.rpc.eventserver.event('strawpoll-complete', data)
def process_card(card, expansion, houses, include_reminder=False): # sanitise card name filtered = clean_text(card.get('internalname', card["card_title"])) if not re_check.match(filtered): print("Still some junk left in name %s (%s)" % (card.get('internalname', card["card_title"]), json.dumps(filtered))) print(json.dumps(card)) sys.exit(1) def build_description(): yield card['card_title'] yield ' [' if card['house'] in houses: yield houses[card['house']]['name'] else: yield card['house'] if card.get('amber'): yield '\u2014' yield str(card['amber']) yield '<A>' yield '] | ' yield card['card_type'] if card.get('traits'): yield ' \u2014 ' yield card['traits'] if card.get('power') or card.get('armor') or card['card_type'] == "Creature": yield ' [' yield str(card['power']) if card.get('armor'): yield '/' yield str(card['armor']) yield ']' if card.get('card_text'): yield ' | ' yield process_text(card['card_text'], include_reminder) desc = ''.join(build_description()) desc = re_multiplespaces.sub(' ', desc).strip() desc = utils.trim_length(desc) numbers = card['card_number'] if card.get('card_number') else [] if not isinstance(numbers, list): numbers = [numbers] numbers = [(expansion['code'].lower(), str(i)) for i in numbers] hidden = 'internalname' in card return filtered, card['card_title'], desc, numbers, hidden
def polls(lrrbot, conn, event, respond_to): """ Command: !polls Section: misc List all currently active polls. """ if not lrrbot.polls: return conn.privmsg(respond_to, "No active polls.") now = time.time() messages = [] for end, title, poll_id, respond_to, tag in lrrbot.polls: messages += [ "%s (https://www.strawpoll.me/%s%s): %s from now" % (title, poll_id, space.SPACE, common.time.nice_duration(end - now, 1)) ] conn.privmsg(respond_to, utils.trim_length("Active polls: " + "; ".join(messages)))
def process_single_card(card, expansion, include_reminder=False): # sanitise card name filtered = clean_text(card.get('internalname', card["name"])) if not re_check.match(filtered): print("Still some junk left in name %s (%s)" % (card.get('internalname', card["name"]), json.dumps(filtered))) print(json.dumps(card)) sys.exit(1) def build_description(): yield card['name'] if 'manaCost' in card: yield ' [' yield re_mana.sub(r"\1", card['manaCost']) yield ']' if card.get('layout') == 'flip': if card['name'] == card['names'][0]: yield ' (flip: ' yield card['names'][1] yield ')' else: yield ' (unflip: ' yield card['names'][0] yield ')' elif card.get('layout') == 'transform': if card['name'] == card['names'][0]: yield ' (back: ' yield card['names'][1] yield ')' else: yield ' (front: ' yield card['names'][0] yield ')' elif card.get('layout') == 'meld': card_a, melded_card, card_b = card['names'] if card['name'] in (card_a, card_b): # mtgjson is inconsistent as to which of these is which # check if "melds with cardname" is in the card text if card['name'] == card_a: other_card = card_b else: other_card = card_a if '(Melds with %s.)' % other_card in card['text']: yield ' (melds with: ' yield other_card yield '; into: ' yield melded_card yield ')' else: # The names of what this melds with and into are in the rules text pass elif card['name'] == melded_card: yield ' (melds from: ' yield card_a yield '; ' yield card_b yield ')' yield ' | ' yield card.get('type', '?Type missing?') if 'power' in card or 'toughness' in card: yield ' [' yield card.get('power', '?') yield '/' yield card.get('toughness', '?') yield ']' if 'loyalty' in card: yield ' [' yield str(card['loyalty']) yield ']' if 'hand' in card or 'life' in card: yield ' [hand: ' if 'hand' in card: yield card['hand'] else: yield "?" yield ', life: ' if 'life' in card: yield card['life'] else: yield "?" yield ']' if 'text' in card: yield ' | ' yield process_text(card['text'], include_reminder) desc = ''.join(build_description()) desc = re_multiplespaces.sub(' ', desc).strip() desc = utils.trim_length(desc) if card.get('layout') == 'flip' and card['name'] != card['names'][0]: multiverseids = numbers = [] else: if card.get('layout') == 'transform': if card['name'] == card['names'][0]: if card.get('number') and 'a' not in card['number'] and 'b' not in card['number']: card['number'] = [card['number'], card['number'] + 'a'] else: if card.get('number') and 'a' not in card['number'] and 'b' not in card['number']: card['number'] = card['number'] + 'b' card['foreignData'] = [] # mtgjson doesn't seem to have accurate foreign multiverse ids for back faces multiverseids = [card['multiverseId']] if card.get('multiverseId') else [] # disabling adding foreign multiverse ids unless we decide we want them for some reason # they add a lot of time to the running of this script #for lang in card.get('foreignData', []): # if lang.get('multiverseId'): # multiverseids.append(lang['multiverseId']) numbers = card['number'] if card.get('number') else [] if not isinstance(numbers, list): numbers = [numbers] hidden = 'internalname' in card # if a card has multiple variants, make "number" entries for the variants # to match what sort of thing we'd be seeing on scryfall if len(multiverseids) > 1 and len(numbers) == 1: orig_number = numbers[0] for i in range(len(multiverseids)): numbers.append(orig_number + chr(ord('a') + i)) numbers = [(expansion['code'].lower(), i) for i in numbers] return filtered, card['name'], desc, multiverseids, numbers, hidden
def process_single_card(card, expansion, include_reminder=False): # sanitise card name filtered = clean_text(card.get('internalname', card["name"])) if not re_check.match(filtered): print("Still some junk left in name %s (%s)" % (card.get('internalname', card["name"]), json.dumps(filtered))) print(json.dumps(card)) sys.exit(1) def build_description(): yield card['name'] if 'manaCost' in card: yield ' [' yield re_mana.sub(r"\1", card['manaCost']) yield ']' if card.get('layout') == 'flip': if card['name'] == card['names'][0]: yield ' (flip: ' yield card['names'][1] yield ')' else: yield ' (unflip: ' yield card['names'][0] yield ')' elif card.get('layout') == 'transform': if card['name'] == card['names'][0]: yield ' (back: ' yield card['names'][1] yield ')' else: yield ' (front: ' yield card['names'][0] yield ')' elif card.get('layout') == 'meld': card_a, melded_card, card_b = card['names'] if card['name'] in (card_a, card_b): # mtgjson is inconsistent as to which of these is which # check if "melds with cardname" is in the card text if card['name'] == card_a: other_card = card_b else: other_card = card_a if '(Melds with %s.)' % other_card in card['text']: yield ' (melds with: ' yield other_card yield '; into: ' yield melded_card yield ')' else: # The names of what this melds with and into are in the rules text pass elif card['name'] == melded_card: yield ' (melds from: ' yield card_a yield '; ' yield card_b yield ')' yield ' | ' yield card.get('type', '?Type missing?') if 'power' in card or 'toughness' in card: yield ' [' yield card.get('power', '?') yield '/' yield card.get('toughness', '?') yield ']' if 'loyalty' in card: yield ' [' yield str(card['loyalty']) yield ']' if 'hand' in card or 'life' in card: yield ' [hand: ' if 'hand' in card: yield card['hand'] else: yield "?" yield ', life: ' if 'life' in card: yield card['life'] else: yield "?" yield ']' if 'text' in card: yield ' | ' yield process_text(card['text'], include_reminder) desc = ''.join(build_description()) desc = re_multiplespaces.sub(' ', desc).strip() desc = utils.trim_length(desc) if card.get('layout') == 'flip' and card['name'] != card['names'][0]: multiverseids = numbers = [] else: if card.get('layout') == 'transform': if card['name'] == card['names'][0]: if card.get('number') and 'a' not in card[ 'number'] and 'b' not in card['number']: card['number'] = [card['number'], card['number'] + 'a'] else: if card.get('number') and 'a' not in card[ 'number'] and 'b' not in card['number']: card['number'] = card['number'] + 'b' card['foreignData'] = [ ] # mtgjson doesn't seem to have accurate foreign multiverse ids for back faces multiverseids = [card['multiverseId'] ] if card.get('multiverseId') else [] # disabling adding foreign multiverse ids unless we decide we want them for some reason # they add a lot of time to the running of this script #for lang in card.get('foreignData', []): # if lang.get('multiverseId'): # multiverseids.append(lang['multiverseId']) numbers = card['number'] if card.get('number') else [] if not isinstance(numbers, list): numbers = [numbers] hidden = 'internalname' in card # if a card has multiple variants, make "number" entries for the variants # to match what sort of thing we'd be seeing on scryfall if len(multiverseids) > 1 and len(numbers) == 1: orig_number = numbers[0] for i in range(len(multiverseids)): numbers.append(orig_number + chr(ord('a') + i)) numbers = [(expansion['code'].lower(), i) for i in numbers] return filtered, card['name'], desc, multiverseids, numbers, hidden
def process_single_card(card, expansion, include_reminder=False): # sanitise card name filtered = clean_text(card.get('internalname', card["name"])) if not re_check.match(filtered): print("Still some junk left in name %s (%s)" % (card.get('internalname', card["name"]), json.dumps(filtered))) print(json.dumps(card)) sys.exit(1) def build_description(): yield card['name'] if 'manaCost' in card: yield ' [' yield re_mana.sub(r"\1", card['manaCost']) yield ']' if card.get('layout') == 'flip': if card['name'] == card['names'][0]: yield ' (flip: ' yield card['names'][1] yield ')' else: yield ' (unflip: ' yield card['names'][0] yield ')' elif card.get('layout') == 'double-faced': if card['name'] == card['names'][0]: yield ' (back: ' yield card['names'][1] yield ')' else: yield ' (front: ' yield card['names'][0] yield ')' elif card.get('layout') == 'meld': top_card, bottom_card, melded_card = card['names'] if card['name'] == top_card: # The names of what this melds with and into are in the card text pass elif card['name'] == bottom_card: yield ' (melds with: ' yield top_card yield '; into: ' yield melded_card yield ')' elif card['name'] == melded_card: yield ' (melds from: ' yield top_card yield '; ' yield bottom_card yield ')' yield ' | ' yield card.get('type', '?Type missing?') if 'power' in card or 'toughness' in card: yield ' [' yield card.get('power', '?') yield '/' yield card.get('toughness', '?') yield ']' if 'loyalty' in card: yield ' [' yield str(card['loyalty']) yield ']' if 'hand' in card or 'life' in card: yield ' [hand: ' if 'hand' in card: yield "%+d" % card['hand'] else: yield "?" yield ', life: ' if 'life' in card: yield "%+d" % card['life'] else: yield "?" yield ']' if 'text' in card: yield ' | ' text = card['text'] # Let Un-set cards keep their reminder text, since there's joeks in there if not include_reminder: text = re_remindertext.sub( lambda match: ' ' if match.group(1) and match.group(2) else '', text) yield re_newlines.sub(' / ', text.strip()) desc = ''.join(build_description()) desc = re_multiplespaces.sub(' ', desc).strip() desc = utils.trim_length(desc) if card.get('layout') == 'flip' and card['name'] != card['names'][0]: multiverseids = numbers = [] else: multiverseids = [card['multiverseid'] ] if card.get('multiverseid') else [] if card.get('variations'): multiverseids.extend(card['variations']) numbers = card['number'] if card.get('number') else [] if not isinstance(numbers, list): numbers = [numbers] hidden = 'internalname' in card # if a card has multiple variants, make "number" entries for the variants # to match what sort of thing we'd be seeing on scryfall if len(multiverseids) > 1 and len(numbers) == 1: orig_number = numbers[0] for i in range(len(multiverseids)): numbers.append(orig_number + chr(ord('a') + i)) numbers = [(expansion['code'].lower(), i) for i in numbers] return filtered, card['name'], desc, multiverseids, numbers, hidden
def get_next_event_text(calendar, after=None, include_current=None, tz=None, verbose=True): """ Build the actual human-readable response to the !next command. The tz parameter can override the timezone used to display the event times. This can be an actual timezone object, or a string timezone name. Defaults to moonbase time. """ if after is None: after = datetime.datetime.now(datetime.timezone.utc) if not tz: tz = config['timezone'] elif isinstance(tz, str): tz = tz.strip() try: tz = common.time.get_timezone(tz) except pytz.exceptions.UnknownTimeZoneError: return "Unknown timezone: %s" % tz events = get_next_event(calendar, after=after, include_current=include_current) if not events: return "There don't seem to be any upcoming scheduled streams" concise_strs = [] verbose_strs = [] for i, ev in enumerate(events): title = ev['title'] if ev['location'] is not None: title += " (%(location)s%(space)s)" % { "location": ev["location"], "space": space.SPACE, } concise_title = title if ev['description'] is not None: title += " (%(description)s)" % { "description": textwrap.shorten(process_description(ev['description']), 200), } # If several events are at the same time, just show the time once after all of them if i == len(events) - 1 or ev['start'] != events[i+1]['start']: if verbose: if ev['start'] < after: nice_duration = common.time.nice_duration(after - ev['start'], 1) + " ago" else: nice_duration = common.time.nice_duration(ev['start'] - after, 1) + " from now" start = ev['start'].astimezone(tz).strftime(DISPLAY_FORMAT) concise_strs.append("%s at %s (%s)" % (concise_title, start, nice_duration)) verbose_strs.append("%s at %s (%s)" % (title, start, nice_duration)) else: concise_strs.append("%s at %s" % (ev['title'], ev['start'].astimezone(tz).strftime(DISPLAY_FORMAT))) else: concise_strs.append(concise_title if verbose else ev['title']) verbose_strs.append(title) if verbose: for strs in [verbose_strs, concise_strs]: if calendar == CALENDAR_LRL: response = "Next scheduled stream: %s." % ", ".join(strs) elif calendar == CALENDAR_FAN: response = "Next scheduled fan stream: %s." % ", ".join(strs) if utils.check_length(response): break else: response = ", ".join(concise_strs) return utils.trim_length(response) # For safety