async def time(ctx: MtgContext, *, args: str) -> None: """Current time in location.""" if len(args) == 0: return await ctx.send( '{author}: No location provided. Please type !time followed by the location you want the time for.' .format(author=ctx.author.mention)) try: twentyfour = configuration.get_bool( f'{guild_or_channel_id(ctx.channel)}.use_24h' ) or configuration.get_bool(f'{ctx.channel.id}.use_24h') ts = fetcher.time(args, twentyfour) times_s = '' for t, zones in ts.items(): cities = sorted( set( re.sub('.*/(.*)', '\\1', zone).replace('_', ' ') for zone in zones)) times_s += '{cities}: {t}\n'.format(cities=', '.join(cities), t=t) await ctx.send(times_s) except NotConfiguredException: await ctx.send('The time command has not been configured.') except TooFewItemsException: logging.exception('Exception trying to get the time for %s.', args) await ctx.send( '{author}: Location not found.'.format(author=ctx.author.mention))
def in_rotation() -> bool: if configuration.get_bool('always_show_rotation'): return True until_full_rotation = next_rotation() - dtutil.now() until_supplemental_rotation = next_supplemental() - dtutil.now() return until_full_rotation < datetime.timedelta( 7) or until_supplemental_rotation < datetime.timedelta(7)
def __init__(self, interestingness: Optional[str] = None, rotation_query: Optional[str] = None, only_these: Optional[List[str]] = None) -> None: super().__init__() self.playability = card.playability() until_full_rotation = rotation.next_rotation() - dtutil.now() until_supplemental_rotation = rotation.next_supplemental() - dtutil.now() in_rotation = configuration.get_bool('always_show_rotation') if until_full_rotation < datetime.timedelta(7): in_rotation = True self.rotation_msg = 'Full rotation is in progress, ends ' + dtutil.display_date(rotation.next_rotation(), 2) elif until_supplemental_rotation < datetime.timedelta(7): in_rotation = True self.rotation_msg = 'Supplemental rotation is in progress, ends ' + dtutil.display_date(rotation.next_supplemental(), 2) elif until_full_rotation < until_supplemental_rotation: self.rotation_msg = 'Full rotation is ' + dtutil.display_date(rotation.next_rotation(), 2) else: self.rotation_msg = 'Supplemental rotation is ' + dtutil.display_date(rotation.next_supplemental(), 2) self.cards: List[Card] = [] if in_rotation: self.read_rotation_files() self.show_interesting = True if interestingness: self.cards = [c for c in self.cards if c.get('interestingness') == interestingness] if only_these: self.cards = [c for c in self.cards if c.name in only_these] self.num_cards = len(self.cards) self.rotation_query = rotation_query or ''
def robots_txt(self) -> Response: """ Serves the robots.txt """ if configuration.get_bool('is_test_site'): return send_from_directory(self.static_folder, 'deny-all-robots.txt') return send_from_directory(self.static_folder, 'robots.txt')
def __init__(self, interestingness: Optional[str] = None, query: Optional[str] = '') -> None: super().__init__() until_rotation = seasons.next_rotation() - dtutil.now() in_rotation = configuration.get_bool('always_show_rotation') if until_rotation < datetime.timedelta(7): in_rotation = True self.rotation_msg = 'Rotation is in progress, ends ' + dtutil.display_date(seasons.next_rotation(), 2) else: self.rotation_msg = 'Rotation is ' + dtutil.display_date(seasons.next_rotation(), 2) if in_rotation: self.in_rotation = in_rotation self.show_interestingness_filter = True self.runs, self.runs_percent, self.cards = rotation.read_rotation_files() # Now add interestingness to the cards, which only decksite knows not magic.rotation. playability = card.playability() c: Card for c in self.cards: c.interestingness = rotation.interesting(playability, c) else: self.cards = [] self.show_interesting = True if interestingness: self.cards = [c for c in self.cards if c.get('interestingness') == interestingness] self.num_cards = len(self.cards) self.query = query self.show_filters_toggle = True self.cards = [c for c in self.cards if visible(c)]
def __init__(self) -> None: super().__init__() until_rotation = seasons.next_rotation() - dtutil.now() in_rotation = configuration.get_bool('always_show_rotation') if until_rotation < datetime.timedelta(7): in_rotation = True self.rotation_msg = 'Rotation is in progress, ends ' + dtutil.display_date( seasons.next_rotation(), 2) else: self.rotation_msg = 'Rotation is ' + dtutil.display_date( seasons.next_rotation(), 2) if in_rotation: self.in_rotation = in_rotation self.runs, self.runs_percent, self.cards = rotation.read_rotation_files( ) else: self.cards = [] self.num_cards = len(self.cards)
def legal_cards(force: bool = False, season: str = None) -> List[str]: if season is None: url = 'legal_cards.txt' else: url = '{season}_legal_cards.txt'.format(season=season) encoding = 'utf-8' if season != 'EMN' else 'latin-1' # EMN was encoded weirdly. cached_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'legal_cards') if os.path.exists(os.path.join(cached_path, url)): h = open(os.path.join(cached_path, url), encoding=encoding) legal = h.readlines() h.close() return [l.strip() for l in legal] url = 'http://pdmtgo.com/' + url legal_txt = fetch_tools.fetch(url, encoding, force=force) if season is not None and configuration.get_bool('save_historic_legal_lists'): with open(os.path.join(cached_path, f'{season}_legal_cards.txt'), 'w', encoding=encoding) as h: h.write(legal_txt) return legal_txt.strip().split('\n')
async def legal_cards_async(season: str = None) -> List[str]: if season is None: url = 'legal_cards.txt' else: url = '{season}_legal_cards.txt'.format(season=season) encoding = 'utf-8' cached_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'legal_cards') if os.path.exists(os.path.join(cached_path, url)): h = open(os.path.join(cached_path, url), encoding=encoding) legal = h.readlines() h.close() return [l.strip() for l in legal] url = 'https://pennydreadfulmtg.github.io/' + url legal_txt = await fetch_tools.fetch_async(url) if season is not None and configuration.get_bool('save_historic_legal_lists'): with open(os.path.join(cached_path, f'{season}_legal_cards.txt'), 'w', encoding=encoding) as h: h.write(legal_txt) return legal_txt.strip().split('\n')
def __init__(self, interestingness: Optional[str] = None, query: Optional[str] = '') -> None: super().__init__() until_full_rotation = rotation.next_rotation() - dtutil.now() until_supplemental_rotation = rotation.next_supplemental() - dtutil.now() in_rotation = configuration.get_bool('always_show_rotation') if until_full_rotation < datetime.timedelta(7): in_rotation = True self.rotation_msg = 'Full rotation is in progress, ends ' + dtutil.display_date(rotation.next_rotation(), 2) elif until_supplemental_rotation < datetime.timedelta(7): in_rotation = True self.rotation_msg = 'Supplemental rotation is in progress, ends ' + dtutil.display_date(rotation.next_supplemental(), 2) elif until_full_rotation < until_supplemental_rotation: self.rotation_msg = 'Full rotation is ' + dtutil.display_date(rotation.next_rotation(), 2) else: self.rotation_msg = 'Supplemental rotation is ' + dtutil.display_date(rotation.next_supplemental(), 2) if in_rotation: self.in_rotation = in_rotation self.show_interestingness_filter = True self.runs, self.runs_percent, self.cards = rotation.read_rotation_files() # Now add interestingness to the cards, which only decksite knows not magic.rotation. playability = card.playability() c: Card for c in self.cards: c.interestingness = rotation.interesting(playability, c) else: self.cards = [] self.show_interesting = True if interestingness: self.cards = [c for c in self.cards if c.get('interestingness') == interestingness] self.num_cards = len(self.cards) self.query = query for c in self.cards: if c.status != 'Undecided': continue c.hits = redact(c.hits) c.hits_needed = redact(c.hits_needed) c.percent = redact(c.percent) c.percent_needed = redact(c.percent_needed) self.show_filters_toggle = True
def create_issue(content: str, author: str, location: str = 'Discord', repo_name: str = 'PennyDreadfulMTG/Penny-Dreadful-Tools', exception: Optional[BaseException] = None) -> Issue: labels: List[str] = [] issue_hash = None if content is None or content == '': return None body = '' if '\n' in content: title, body = content.split('\n', 1) body += '\n\n' else: title = content body += 'Reported on {location} by {author}\n\n'.format(location=location, author=author) if request: body += '--------------------------------------------------------------------------------\n' body += '<details><summary><strong>Request Data</strong></summary>\n```\n' body += textwrap.dedent(""" Request Method: {method} Path: {full_path} Cookies: {cookies} Endpoint: {endpoint} View Args: {view_args} Person: {id} Referrer: {referrer} Request Data: {safe_data} """.format(method=request.method, full_path=request.full_path, cookies=request.cookies, endpoint=request.endpoint, view_args=request.view_args, id=session.get('id', 'logged_out'), referrer=request.referrer, safe_data=str(safe_data(request.form)))) body += '\n'.join( ['{k}: {v}'.format(k=k, v=v) for k, v in request.headers]) body += '\n```\n</details>\n' ua = request.headers.get('User-Agent', '') if ua == 'pennydreadfulmagic.com cache renewer': labels.append(ua) elif 'YandexBot' in ua or 'Googlebot' in ua or 'bingbot' in ua: labels.append('Search Engine') if exception: body += '--------------------------------------------------------------------------------\n' body += '<details><summary>\n' body += exception.__class__.__name__ + '\n' body += str(exception) + '\n' body += '</summary>\n' stack = traceback.extract_stack()[:-3] + traceback.extract_tb( exception.__traceback__) pretty = traceback.format_list(stack) body += 'Stack Trace:\n\n```Python traceback\n' + ''.join( pretty) + '\n```\n\n</details>\n' issue_hash = hashlib.sha1(''.join(pretty).encode()).hexdigest() body += f'Exception_hash: {issue_hash}\n' elif repo_name == 'PennyDreadfulMTG/perf-reports': stack = traceback.extract_stack()[:-3] pretty = traceback.format_list(stack) if request: pretty.append(request.full_path) issue_hash = hashlib.sha1(''.join(pretty).encode()).hexdigest() body += f'Location Hash: {issue_hash}\n' print(title + '\n' + body, file=sys.stderr) # Only check for github details at the last second to get log output even if github not configured. if not configuration.get('github_user') or not configuration.get( 'github_password'): return None if not configuration.get_bool('create_github_issues'): print(f'Not creating github issue:\n{title}\n\n{body}') return None g = Github(configuration.get('github_user'), configuration.get('github_password')) git_repo = g.get_repo(repo_name) if repo_name == 'PennyDreadfulMTG/perf-reports': labels.append(location) if exception: labels.append(exception.__class__.__name__) if issue_hash: try: issue = g.search_issues(issue_hash, repo=repo_name)[0] labelstr = '; '.join(labels) issue.create_comment(f'{title}\n\n{body}\n\nLabels: {labelstr}') return issue except IndexError: pass issue = git_repo.create_issue(title=title, body=body, labels=labels) return issue
import sys import time from typing import List, Optional from generate_readme import generate_readme from shared import configuration from shared.pd_exception import InvalidArgumentException, TestFailedException try: from plumbum import FG, local from plumbum.commands.processes import ProcessExecutionError except ImportError: sys.stderr.write('Please run ./dev.py build\n') ON_PROD = configuration.get_bool('production') if ON_PROD: sys.stderr.write('DO NOT RUN dev.py ON PROD\n') sys.exit(1) ON_WINDOWS = sys.platform == 'win32' def run() -> None: try: try: exit_code = None run_dangerously() except InvalidArgumentException as e: exit_code = 1 raise except TestFailedException as e:
def test_500() -> Response: if configuration.get_bool('production'): return return_json(generate_error( 'ON_PROD', 'This only works on test environments'), status=404) raise TooManyItemsException()
def in_rotation() -> bool: if configuration.get_bool('always_show_rotation'): return True until_rotation = seasons.next_rotation() - dtutil.now() return until_rotation < datetime.timedelta(7)
def robots_txt(self): if configuration.get_bool('is_test_site'): return send_from_directory(self.static_folder, 'deny-all-robots.txt') return send_from_directory(self.static_folder, 'robots.txt')