From 114de79efad436d13d820d1297078df1419a3b9f Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Tue, 16 May 2023 18:57:07 -0700 Subject: [PATCH 01/18] WIP restructuring --- src/catchup.py | 7 ++----- src/main.py | 48 +++++++++------------------------------------ src/talent_lists.py | 2 +- 3 files changed, 12 insertions(+), 45 deletions(-) diff --git a/src/catchup.py b/src/catchup.py index b258ca7..759cf22 100644 --- a/src/catchup.py +++ b/src/catchup.py @@ -18,7 +18,7 @@ import talenttweet as tt import ttweetqueue as ttq PROGRAM_ARGS = None -safe_to_post_tweets = True +safe_to_post_tweets = False errored = False ## Returns the ID of all tweets (up to limit) from a user ID. @@ -133,13 +133,10 @@ async def process_queue() -> bool: # return True = no problems # return False = issue occurred where we couldn't post all past tweets properly -async def run(program_args): - global PROGRAM_ARGS +async def run(): global errored global safe_to_post_tweets - PROGRAM_ARGS = program_args - ret = None queue = ttq.TalentTweetQueue.instance while True: await get_cross_talent_tweets() diff --git a/src/main.py b/src/main.py index ea900e2..01eba94 100644 --- a/src/main.py +++ b/src/main.py @@ -24,9 +24,6 @@ def init_argparse(): p = argparse.ArgumentParser(description='Twitter bot that follows interactions between Nijisanji EN/ID and hololive EN/ID members.', formatter_class=RawTextHelpFormatter) p.add_argument('mode', nargs='?', \ help=MODES_HELP_STR) - p.add_argument('--show-tokens', action='store_true', help='[DO NOT USE IN PUBLIC SETTING] print stored tokens from secrets.ini') - p.add_argument('--announce-catchup', action='store_true', help='In catch-up mode, post a tweet announcing catch-up mode.') - p.add_argument('--auto-listen', action='store_true', help='In catch-up mode, transition to listen mode after successfuly catching up.') p.add_argument('--no-delay', action='store_true', help='In self-destruct mode, clear tweets without safety waiting.') return p @@ -45,56 +42,29 @@ async def self_destruct(): async def async_main(): global PROGRAM_ARGS - ## Determine running mode - # match PROGRAM_ARGS.mode.lower(): - # case 'l' | 'listen': - # print('RUNNING IN LISTEN MODE\n') - # await listen.run() - # case 'c' | 'catchup': - # print('RUNNING IN CATCH-UP MODE\n') - # if await catchup.run(PROGRAM_ARGS) and PROGRAM_ARGS.auto_listen: - # print('CATCH-UP MODE DONE, GOING INTO LISTEN MODE') - # await listen.run() - # case 'd' | 'delete-all': - # print('WARNING: SELF-DESTRUCT MODE') - # await self_destruct() - # case 'cmd': - # command_line() - # case _: - # print('\ninvalid mode. run with no arguments or "-h" for help page, including mode list.') - # return + if PROGRAM_ARGS.mode == None: + await catchup.run() + return + mode = PROGRAM_ARGS.mode.lower() - if mode in ['l', 'listen']: - print('RUNNING IN LISTEN MODE') - await listen.run() - elif mode in ['c', 'catchup']: - print('RUNNING IN CATCH UP MODE') - if await catchup.run(PROGRAM_ARGS) and PROGRAM_ARGS.auto_listen: - print('CATCH UP MODE DONE, GOING INTO LISTEN MODE') - listen.run() - elif mode in ['d', 'delete-all']: + if mode in ['d', 'delete-all']: print('WARNING: SELF-DESTRUCT MODE') await self_destruct() elif mode == 'cmd': command_line() else: - print('\ninvalid mode. run with no arguments or -h for help and modes') + print('\nunknown mode. run with no arguments or -h for help and modes') def main(): global PROGRAM_ARGS parser = init_argparse() - if len(sys.argv) < 2: - parser.print_help() - return + # if len(sys.argv) < 2: + # parser.print_help() + # return PROGRAM_ARGS = parser.parse_args() - if PROGRAM_ARGS.show_tokens: - print(api_secrets.get_all_secrets()) - - if PROGRAM_ARGS.mode is None: return - ## We expect to run in some mode now. # Initialize shared API instance diff --git a/src/talent_lists.py b/src/talent_lists.py index 5eb4fbf..f72e4e8 100644 --- a/src/talent_lists.py +++ b/src/talent_lists.py @@ -18,7 +18,7 @@ def __create_dict(file, _dict, company): words = line.split() if len(words) == 2 and line[0] != '#': name, id = line.split() - name = f'{util.get_username_online(id, default=name)}' # attempt to get updated name + # name = f'{util.get_username_online(id, default=name)}' # attempt to get updated name talents[int(id)] = name _dict[int(id)] = name talents_company[int(id)] = company From 01de42a73d93cdc10da2aa6edc327516fe73a6fc Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Tue, 16 May 2023 19:18:12 -0700 Subject: [PATCH 02/18] swap user ID and username in talent lists --- lists/holoen.txt | 42 +++++++++++++-------------- lists/holoid.txt | 18 ++++++------ lists/nijien.txt | 70 ++++++++++++++++++++++----------------------- lists/nijiexid.txt | 44 ++++++++++++++-------------- src/talent_lists.py | 2 +- 5 files changed, 88 insertions(+), 88 deletions(-) diff --git a/lists/holoen.txt b/lists/holoen.txt index a01342b..6055c84 100644 --- a/lists/holoen.txt +++ b/lists/holoen.txt @@ -3,36 +3,36 @@ # ----- hololive EN ----- # --- [Myth] --- -gawrgura 1283657064410017793 -watsonameliaen 1283656034305769472 -moricalliope 1283653858510598144 -ninomaeinanis 1283650008835743744 -takanashikiara 1283646922406760448 +1283657064410017793 gawrgura +1283656034305769472 watsonameliaen +1283653858510598144 moricalliope +1283650008835743744 ninomaeinanis +1283646922406760448 takanashikiara # --- [HOPE] --- -irys_en 1363705980261855232 +1363705980261855232 irys_en # --- [Council] --- -hakosbaelz 1409783149211443200 -ourokronii 1409817096523968513 -ceresfauna 1409784760805650436 -tsukumosana 1409819816194576394 -nanashimumei_en 1409817941705515015 +1409783149211443200 hakosbaelz +1409817096523968513 ourokronii +1409784760805650436 ceresfauna +1409819816194576394 tsukumosana +1409817941705515015 nanashimumei_en # ----- HOLOSTARS EN ----- # --- [TEMPUS] --- # Gen 1 -noirvesper_en 1536579341332516864 -axelsyrios 1536577295632441344 -magnidezmond 1536576325296996352 -regisaltare 1536575088996524032 +1536579341332516864 noirvesper_en +1536577295632441344 axelsyrios +1536576325296996352 magnidezmond +1536575088996524032 regisaltare # Gen 2 -gavisbettel 1582926739684339712 -machinaxflayon 1582922712166825986 -banzoinhakka 1582927907206631425 -josuijishinri 1582925071546732544 +1582926739684339712 gavisbettel +1582922712166825986 machinaxflayon +1582927907206631425 banzoinhakka +1582925071546732544 josuijishinri # --- STAFF --- -omegaalpha_en 1397148959798226945 -hololivepro_EN 1540204458042621952 +1397148959798226945 omegaalpha_en +1540204458042621952 hololivepro_EN diff --git a/lists/holoid.txt b/lists/holoid.txt index 45c313c..091df7e 100644 --- a/lists/holoid.txt +++ b/lists/holoid.txt @@ -3,16 +3,16 @@ # ----- hololive ID ----- # --- [Gen 1] --- -ayunda_risu 1234752200145899520 -moonahoshinova 1234753886520393729 -airaniiofifteen 1235180878449397764 +1234752200145899520 ayunda_risu +1234753886520393729 moonahoshinova +1235180878449397764 airaniiofifteen # --- [Gen 2] --- -pavoliareine 1328275136575799297 -kureijiollie 1328277233492844544 -anyamelfissa 1328277750000492545 +1328275136575799297 pavoliareine +1328277233492844544 kureijiollie +1328277750000492545 anyamelfissa # --- [Gen 3] --- -kobokanaeru 1486629076005634049 -vestiazeta 1486633489101307907 -kaelakovalskia 1486636197908602880 \ No newline at end of file +1486629076005634049 kobokanaeru +1486633489101307907 vestiazeta +1486636197908602880 kaelakovalskia \ No newline at end of file diff --git a/lists/nijien.txt b/lists/nijien.txt index 0918e7c..14044ba 100644 --- a/lists/nijien.txt +++ b/lists/nijien.txt @@ -3,54 +3,54 @@ # ----- [NIJISANJI EN] ----- # --- [Lazulight] --- -PomuRainpuff 1390637197167038464 -EliraPendora 1390620618001838086 -FinanaRyugu 1390209302120394754 +1390637197167038464 PomuRainpuff +1390620618001838086 EliraPendora +1390209302120394754 FinanaRyugu # --- [Obsydia] --- -Petra_Gurin 1413339084076978179 -Selen_Tatsuki 1413318241804439552 -Rosemi_Lovelock 1413326894435602434 +1413339084076978179 Petra_Gurin +1413318241804439552 Selen_Tatsuki +1413326894435602434 Rosemi_Lovelock # --- [Ethyria] --- -MillieParfait 1437952405283426310 -EnnaAlouette 1437963160544284675 -NinaKosaka 1437959162651156484 -ReimuEndou 1437961007029227520 +1437952405283426310 MillieParfait +1437963160544284675 EnnaAlouette +1437959162651156484 NinaKosaka +1437961007029227520 ReimuEndou # --- [Luxiem]--- -Vox_Akuma 1465851881180348425 -shu_amino 1465850835951357955 -ike_eveland 1465851188562345985 -Mysta_Rias 1465851243167895554 -luca_kaneshiro 1465858739970273281 +1465851881180348425 Vox_Akuma +1465850835951357955 shu_amino +1465851188562345985 ike_eveland +1465851243167895554 Mysta_Rias +1465858739970273281 luca_kaneshiro # --- [Noctyx] --- -alban_knox 1490867613915828224 -uki_violeta 1491195742123397124 -Yugo_Asuma 1492604168145539072 -Fulgur_Ovid 1493392149664219138 -sonny_brisko 1493394108014292993 +1490867613915828224 alban_knox +1491195742123397124 uki_violeta +1492604168145539072 Yugo_Asuma +1493392149664219138 Fulgur_Ovid +1493394108014292993 sonny_brisko # --- [ILUNA] --- -MariaMari0nette 1545351225293426688 -AsterArcadia 1545352592884084736 -ScarleYonaguni 1545354510515654656 -KyoKanek0 1545552756773208066 -AiaAmare 1545562635650957312 -RenZott0 1546328834559340544 +1545351225293426688 MariaMari0nette +1545352592884084736 AsterArcadia +1545354510515654656 ScarleYonaguni +1545552756773208066 KyoKanek0 +1545562635650957312 AiaAmare +1546328834559340544 RenZott0 # --- [XSOLEIL] --- -MelocoKyoran 1589536631324692480 -HexHaywire 1589524401170833409 -D_Dropscythe 1589531775058968576 -ZaionLanZa 1589539582399348738 -KotokaTorahime 1591995159901663232 -Ver_Vermillion 1589791076709171201 +1589536631324692480 MelocoKyoran +1589524401170833409 HexHaywire +1589531775058968576 D_Dropscythe +1589539582399348738 ZaionLanZa +1591995159901663232 KotokaTorahime +1589791076709171201 Ver_Vermillion # --- [ALTS] --- -3W1W4 1507066475638673422 -RyuguFinana 1506863869901168642 +1507066475638673422 3W1W4 +1506863869901168642 RyuguFinana # --- [STAFF] --- -NIJISANJI_World 1214737620749578240 \ No newline at end of file +1214737620749578240 NIJISANJI_World \ No newline at end of file diff --git a/lists/nijiexid.txt b/lists/nijiexid.txt index b60ce90..9e21c46 100644 --- a/lists/nijiexid.txt +++ b/lists/nijiexid.txt @@ -2,31 +2,31 @@ # ----- [NIJISANJI ex-ID] ----- -ZEA_Cornelia 1165866976192823297 -Hana_Macchia 1165866977472024576 -Taka_Radjiman 1165866977715347456 -# graduated MiyuOttavia 1205742630467801088 -RiksaDhirendra 1205743596785127424 -Rai_Galilei 1205744131294654466 -AmiciaMichella 1205744430386315265 -Azura_Cecillia 1237600624646078464 -Nara_Haramaung 1237603606448058371 -LaylaAlstro2434 1237613895675596800 -# elonmusk'd 1290243278814683137 -Bonnivier_2434 1587724357496815616 -Etna_Crimson 1290243331629318144 -SiskaLeontyne 1290243510193369089 -DeremKado 1323147415168323586 -NagisaArcinia 1323147843398324225 -RezaAvanluna 1323147856828510208 -HyonaElatiora 1414845791944937476 -Xia_Ekavira 1414845844692504611 -MikaMelatika 1414849131655450626 +1165866976192823297 ZEA_Cornelia +1165866977472024576 Hana_Macchia +1165866977715347456 Taka_Radjiman +# graduated 1205742630467801088 MiyuOttavia +1205743596785127424 RiksaDhirendra +1205744131294654466 Rai_Galilei +1205744430386315265 AmiciaMichella +1237600624646078464 Azura_Cecillia +1237603606448058371 Nara_Haramaung +1237613895675596800 LaylaAlstro2434 +# elonmusk'd 1290243278814683137 bobon_pranaja +1587724357496815616 Bonnivier_2434 +1290243331629318144 Etna_Crimson +1290243510193369089 SiskaLeontyne +1323147415168323586 DeremKado +1323147843398324225 NagisaArcinia +1323147856828510208 RezaAvanluna +1414845791944937476 HyonaElatiora +1414845844692504611 Xia_Ekavira +1414849131655450626 MikaMelatika # --- [ALTS] --- -HanaMacchia2 1405494022446010375 +1405494022446010375 HanaMacchia2 # --- [STAFF] --- -NIJISANJI_ID 1152523848060850177 +1152523848060850177 NIJISANJI_ID diff --git a/src/talent_lists.py b/src/talent_lists.py index f72e4e8..827ca74 100644 --- a/src/talent_lists.py +++ b/src/talent_lists.py @@ -17,7 +17,7 @@ def __create_dict(file, _dict, company): for line in f: words = line.split() if len(words) == 2 and line[0] != '#': - name, id = line.split() + id, name = line.split() # name = f'{util.get_username_online(id, default=name)}' # attempt to get updated name talents[int(id)] = name _dict[int(id)] = name From e657f88ba05c54ef01ca9ace448ebfff4a23c161 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Tue, 16 May 2023 20:02:20 -0700 Subject: [PATCH 03/18] move credentials to .env --- README.md | 16 ++++++++++++++++ requirements.txt | 6 +++--- secrets.ini | 23 ----------------------- src/api_secrets.py | 22 +++++++++++----------- 4 files changed, 30 insertions(+), 37 deletions(-) delete mode 100644 secrets.ini diff --git a/README.md b/README.md index 423d3b9..394e8d0 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,22 @@ Twitter bot that tracks cross-company interactions between the non-JP branches o **This project was created to run [this account](https://twitter.com/NijiHolo_EN_ID).** +## `.env` +These need to be defined in a `.env` file at the project root (outside of `src`): +``` +# Scweet (scraping) +SCWEET_EMAIL= +SCWEET_USERNAME= +SCWEET_PASSWORD= + +# Twitter API bot keys (posting) +api_key= +api_secret= +oauth1_access_token= +oauth1_access_secret= +bearer_token= +``` + ## Running modes The bot may run in these modes: * Catch-up (`c`): intended to run only once, scan all accounts for cross-company tweets and post them. Terminate when done posting all. diff --git a/requirements.txt b/requirements.txt index aa9abf0..f8c83f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ +python-dotenv nest-asyncio pytz +Scweet tweet-capture -tweepy -opencv-python -git+https://github.com/muskit/twint_2022_fix.git \ No newline at end of file +opencv-python \ No newline at end of file diff --git a/secrets.ini b/secrets.ini deleted file mode 100644 index 4289435..0000000 --- a/secrets.ini +++ /dev/null @@ -1,23 +0,0 @@ -## Twitter developer credentials. - -# ---->> MAKE SURE YOUR VALUES AREN'T UPLOADED TO THE REPO! <<---- -# -# This file should be added to .gitignore as a safeguard. -# -# If Git still wants to commit this file after changing its contents, -# force Git to stop tracking the file's changes: -# git update-index --assume-unchanged [ ...] -# -# To resume tracking its changes: -# git update-index --no-assume-unchanged [ ...] -# -# https://stackoverflow.com/questions/10755655/git-ignore-tracked-files - -# note: api_key/secret = consumer_key/secret - -[Credentials] -api_key=xxx -api_secret=yyy -bearer_token=zzz -oauth1_access_token=x -oauth1_access_secret=y diff --git a/src/api_secrets.py b/src/api_secrets.py index 2e50e12..ca13bda 100644 --- a/src/api_secrets.py +++ b/src/api_secrets.py @@ -1,41 +1,41 @@ ## Twitter developer credentials management. -import os -import configparser +from os.path import join, isfile +from dotenv import dotenv_values import util # returns dictionary of the Credentials section. # [NOT TO BE USED OUTSIDE OF THIS FILE.] -def __get_ini_credentials(): - c = configparser.RawConfigParser() - if len(c.read(os.path.join(util.get_project_dir(), 'secrets.ini'))) > 0 and c.has_section('Credentials'): - return c['Credentials'] +def __get_env(): + f = join(util.get_project_dir(), '.env') + if isfile(f): + return dotenv_values(f) return None # returns the consumer api_key stored in secrets.ini def api_key(): - c = __get_ini_credentials() + c = __get_env() return c.get(option='api_key', fallback='xxx') if c is not None else 'xxx' # returns the consumer api_secret stored in secrets.ini def api_secret(): - c = __get_ini_credentials() + c = __get_env() return c.get(option='api_secret', fallback='yyy') if c is not None else 'yyy' # returns the bearer_token stored in secrets.ini def bearer_token(): - c = __get_ini_credentials() + c = __get_env() return c.get(option='bearer_token', fallback='zzz') if c is not None else 'zzz' # returns the access_token stroed in secrets.ini def access_token(): - c = __get_ini_credentials() + c = __get_env() return c.get(option='oauth1_access_token', fallback='zzz') if c is not None else 'aaa' # returns the access_secret stroed in secrets.ini def access_secret(): - c = __get_ini_credentials() + c = __get_env() return c.get(option='oauth1_access_secret', fallback='zzz') if c is not None else 'bbb' def get_all_secrets(): From 9ab486d1bcadd1974f7dc77cfba28bcfe70851b2 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Sun, 13 Aug 2023 16:01:10 -0700 Subject: [PATCH 04/18] welcome, holoAdvent! --- lists/holoen.txt | 6 ++++++ src/talent_lists.py | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lists/holoen.txt b/lists/holoen.txt index 6055c84..80cdc47 100644 --- a/lists/holoen.txt +++ b/lists/holoen.txt @@ -19,6 +19,12 @@ 1409819816194576394 tsukumosana 1409817941705515015 nanashimumei_en +# --- [Advent] --- +1656536840240005121 shiorinovella +1656528951303614464 nerissa_en +1656889310472437761 fuwamoco_en +1656531547279982593 kosekibijou + # ----- HOLOSTARS EN ----- # --- [TEMPUS] --- diff --git a/src/talent_lists.py b/src/talent_lists.py index 827ca74..02ee2d2 100644 --- a/src/talent_lists.py +++ b/src/talent_lists.py @@ -1,11 +1,11 @@ import util -holo_en = dict() -holo_id = dict() -niji_en = dict() -niji_exid = dict() -talents = dict() -talents_company = dict() +holo_en = dict[int, str] +holo_id = dict[int, str] +niji_en = dict[int, str] +niji_exid = dict[int, str] +talents = dict[int, str] +talents_company = dict[int, str] test_talents = dict() @@ -41,6 +41,12 @@ def init(): test_talents = holo_en +def is_niji(id: int) -> bool: + return id in niji_en or id in niji_exid + +def is_holo(id: int) -> bool: + return id in holo_en or id in holo_id + def get_twitter_rules(): global talents rules = list() From 8c7ba26300201394b73644aa28348b37456f65fe Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Mon, 14 Aug 2023 02:47:29 -0700 Subject: [PATCH 05/18] spinning things back up slowly --- .gitignore | 2 +- requirements.txt | 2 +- src/TEST_TWEETY.py | 72 ++++++++++++++++++++++++++++++++++ src/talent_lists.py | 2 + src/talenttweet.py | 94 ++++++++------------------------------------- 5 files changed, 93 insertions(+), 79 deletions(-) create mode 100644 src/TEST_TWEETY.py diff --git a/.gitignore b/.gitignore index 58ba0b2..ac961e8 100644 --- a/.gitignore +++ b/.gitignore @@ -143,5 +143,5 @@ cython_debug/ .vscode # project-specific (secret.ini: can't ignore existing file?) -secrets.ini *.png +session.json \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f8c83f2..e949f7b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ python-dotenv nest-asyncio pytz -Scweet +tweety-ns tweet-capture opencv-python \ No newline at end of file diff --git a/src/TEST_TWEETY.py b/src/TEST_TWEETY.py new file mode 100644 index 0000000..1bce124 --- /dev/null +++ b/src/TEST_TWEETY.py @@ -0,0 +1,72 @@ +from time import sleep +from datetime import datetime, timedelta + +from dotenv import dotenv_values +import pytz + +from tweety import Twitter +from tweety.types import * + +creds = dotenv_values() + +app = Twitter("session") +app.sign_in(creds["username"], creds["password"]) + +def url(t: Tweet): + return f'https://twitter.com/{t.author.username}/status/{t.id}' + +def print_tweets(tweets: list): + print(f'{len(tweets)} tweets:') + for t in tweets: + if isinstance(t, Tweet): + print(f'{t.date} : {url(t)} : RT? {t.is_retweet}') + elif isinstance(t, TweetThread): + print('-----------TTd----------') + print_tweets(t.tweets) + print('-----------end----------') + +def get_tweets_from_user(uid: int | str, since: datetime = None) -> list: + reached_backdate = False + tweets: [Tweet] = [] + if since == None: + since = datetime.utcnow().replace(tzinfo=pytz.utc) - timedelta(days=7) + print(f'Grabbing tweets since 7 days ago ({since.date()})') + + if isinstance(uid, str): + name = uid + uid = app._get_user_id(uid) + print(f"{name} = {uid}") + + def add_tweet(tweet: Tweet): + nonlocal reached_backdate + try: + if tweet.is_retweet or tweet.author.id == uid: + tweets.append(tweet) + if not reached_backdate and tweet.date <= since: + print("reached backdate") + reached_backdate = True + except AttributeError: + print("skipping malformed tweet: {tweet}") + return + + uts = app.get_tweets(uid, replies=True) + while not reached_backdate: + cur_page = uts.tweets + print(f'obtained {len(cur_page)} tweets') + + if len(cur_page) == 0: break + + for e in cur_page: + if isinstance(e, Tweet): + add_tweet(e) + elif isinstance(e, TweetThread): + for t in e.tweets: + add_tweet(t) + + uts.get_next_page() + + tweets.sort(key=lambda t: t.id) + return tweets + +tweets = get_tweets_from_user("ninakosaka", since=datetime(2023, 7, 1)) +print_tweets(tweets) \ No newline at end of file diff --git a/src/talent_lists.py b/src/talent_lists.py index 02ee2d2..51b945b 100644 --- a/src/talent_lists.py +++ b/src/talent_lists.py @@ -47,6 +47,8 @@ def is_niji(id: int) -> bool: def is_holo(id: int) -> bool: return id in holo_en or id in holo_id +# For filtered stream +# DEPRECATED: thx elon def get_twitter_rules(): global talents rules = list() diff --git a/src/talenttweet.py b/src/talenttweet.py index 0df9631..ab91737 100644 --- a/src/talenttweet.py +++ b/src/talenttweet.py @@ -42,72 +42,26 @@ class TalentTweet: tweet_id=tweet_id, author_id=author_id, date_time=date_time, mrq=(mentions, reply_to, quote_retweeted) ) - - @staticmethod - async def create_from_twint_tweet(tweet): - # MRQ - mentions = set() - reply_to = None - quoted_id = None - - # reply_to/mentions - is_reply = tweet.id != int(tweet.conversation_id) - mentions = set([x['id'] for x in tweet.mentions]) - if is_reply and len(tweet.reply_to) > 0: - reply_to = tweet.reply_to[0]['id'] # FIXME: QRT = is_reply and len(tweet.reply_to) == 0? - reply_others = [x['id'] for x in tweet.reply_to[1:]] - mentions.update(reply_others) - try: mentions.remove(reply_to) - except: pass - # qrt - if type(tweet.quote_url) == str: - # print(f'url: {tweet.quote_url} ({type(tweet.quote_url)})') - quote_tokens = tweet.quote_url.split('/') - if len(quote_tokens) >= 2: - quoted_username = quote_tokens[-2] - quoted_id = util.get_user_id_local(quoted_username) - if quoted_id == -1: - quoted_id = util.get_user_id_online(quoted_username) + # Serialized one-liner format: + # {tweet} {author} {time in seconds since epoch} m {mention_set} r {reply_to_author} q {quote_retweet_author} + def serialize(self): + s = f'{self.tweet_id} {self.author_id} {self.date_time.timestamp()} ' - # NOTE: strptime doesn't attach timezone info. - # tweet's datetime will be in local time - date_time = datetime.datetime.strptime(tweet.datetime, '%Y-%m-%d %H:%M:%S %Z') - LOCAL_TIMEZONE = datetime.datetime.now().astimezone().tzinfo - date_time = date_time.replace(tzinfo=LOCAL_TIMEZONE) # attach system local timezone - return TalentTweet(tweet_id=tweet.id, author_id=tweet.user_id, date_time=date_time, mrq=(mentions, reply_to, quoted_id)) - - @staticmethod - def create_from_v2api_response(resp): - tweet = resp.data - if tweet is None: return None + if None not in [self.rt_target, self.rt_author_id]: + s += f'rt {self.rt_target} {self.rt_author_id}' + return s[:-1] # stop here since retweets can't have other info - mrq = twapi.TwAPI.get_mrq(resp) - rt_target = None - rt_author_id = None - - # check if is RT - if tweet.referenced_tweets is not None and len(tweet.referenced_tweets) > 0: - for ref in tweet.referenced_tweets: - if ref.type == 'retweeted': - rt_target = ref.id - for incl_tweet in resp.includes['tweets']: - if incl_tweet.id == ref.id: - rt_author_id = incl_tweet.author_id - - return TalentTweet( - tweet_id=tweet.id, - author_id=tweet.author_id, - date_time=tweet.created_at, - mrq=mrq, - rt_target=rt_target, - rt_author_id=rt_author_id - ) - - @staticmethod - async def create_from_id(id): - resp = await twapi.TwAPI.instance.get_tweet_response(id) - return TalentTweet.create_from_v2api_response(resp) + if len(self.mentions) > 0: + s += 'm ' + for id in self.mentions: + s += f'{id} ' + if self.reply_to: + s += f'r {self.reply_to} ' + if self.quote_retweeted: + s += f'q {self.quote_retweeted} ' + + return s[:-1] def __init__(self, tweet_id: int, author_id: int, date_time: datetime.datetime, mrq: tuple, rt_target: int=None, rt_author_id: int=None): self.tweet_id, self.author_id = tweet_id, author_id @@ -140,20 +94,6 @@ class TalentTweet: f'{self.serialize()}\n' f'======================================================' ) - - # Serialized one-liner format: - # {tweet} {author} {time in seconds since epoch} m {mention_set} r {reply_to_author} q {quote_retweet_author} - def serialize(self): - s = f'{self.tweet_id} {self.author_id} {self.date_time.timestamp()} ' - if len(self.mentions) > 0: - s += 'm ' - for id in self.mentions: - s += f'{id} ' - if self.reply_to: - s += f'r {self.reply_to} ' - if self.quote_retweeted: - s += f'q {self.quote_retweeted} ' - return s[:-1] def is_cross_company(self): for other_id in self.all_parties: From 45ac1af6825e4b245e4f5155063d559c96bcc02e Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Mon, 14 Aug 2023 22:39:47 -0700 Subject: [PATCH 06/18] create the scraper class, reorganizing --- requirements.txt | 1 + src/TEST_TWEETY.py | 72 --------------------------------- src/scraper.py | 99 +++++++++++++++++++++++++++++++++++++++++++++ src/talent_lists.py | 3 ++ src/talenttweet.py | 61 ++++++++++++---------------- src/twapi.py | 45 +-------------------- src/tweety_utils.py | 19 +++++++++ src/util.py | 2 +- 8 files changed, 149 insertions(+), 153 deletions(-) delete mode 100644 src/TEST_TWEETY.py create mode 100644 src/scraper.py create mode 100644 src/tweety_utils.py diff --git a/requirements.txt b/requirements.txt index e949f7b..b705a24 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,6 @@ python-dotenv nest-asyncio pytz tweety-ns +tweepy tweet-capture opencv-python \ No newline at end of file diff --git a/src/TEST_TWEETY.py b/src/TEST_TWEETY.py deleted file mode 100644 index 1bce124..0000000 --- a/src/TEST_TWEETY.py +++ /dev/null @@ -1,72 +0,0 @@ -from time import sleep -from datetime import datetime, timedelta - -from dotenv import dotenv_values -import pytz - -from tweety import Twitter -from tweety.types import * - -creds = dotenv_values() - -app = Twitter("session") -app.sign_in(creds["username"], creds["password"]) - -def url(t: Tweet): - return f'https://twitter.com/{t.author.username}/status/{t.id}' - -def print_tweets(tweets: list): - print(f'{len(tweets)} tweets:') - for t in tweets: - if isinstance(t, Tweet): - print(f'{t.date} : {url(t)} : RT? {t.is_retweet}') - elif isinstance(t, TweetThread): - print('-----------TTd----------') - print_tweets(t.tweets) - print('-----------end----------') - -def get_tweets_from_user(uid: int | str, since: datetime = None) -> list: - reached_backdate = False - tweets: [Tweet] = [] - if since == None: - since = datetime.utcnow().replace(tzinfo=pytz.utc) - timedelta(days=7) - print(f'Grabbing tweets since 7 days ago ({since.date()})') - - if isinstance(uid, str): - name = uid - uid = app._get_user_id(uid) - print(f"{name} = {uid}") - - def add_tweet(tweet: Tweet): - nonlocal reached_backdate - try: - if tweet.is_retweet or tweet.author.id == uid: - tweets.append(tweet) - if not reached_backdate and tweet.date <= since: - print("reached backdate") - reached_backdate = True - except AttributeError: - print("skipping malformed tweet: {tweet}") - return - - uts = app.get_tweets(uid, replies=True) - while not reached_backdate: - cur_page = uts.tweets - print(f'obtained {len(cur_page)} tweets') - - if len(cur_page) == 0: break - - for e in cur_page: - if isinstance(e, Tweet): - add_tweet(e) - elif isinstance(e, TweetThread): - for t in e.tweets: - add_tweet(t) - - uts.get_next_page() - - tweets.sort(key=lambda t: t.id) - return tweets - -tweets = get_tweets_from_user("ninakosaka", since=datetime(2023, 7, 1)) -print_tweets(tweets) \ No newline at end of file diff --git a/src/scraper.py b/src/scraper.py new file mode 100644 index 0000000..c20e6e5 --- /dev/null +++ b/src/scraper.py @@ -0,0 +1,99 @@ +from os.path import exists +from time import sleep +from datetime import datetime, timedelta + +from dotenv import dotenv_values +import pytz + +from tweety import Twitter +from tweety.types import * + +from tweety_utils import * +from talenttweet import * +from talent_lists import is_niji, is_holo + +class Scraper: + def __init__(self): + creds = dotenv_values() + self.app = Twitter("session") + if exists("session.json"): + self.app.connect() + else: + self.app.sign_in(creds["scraper_username"], creds["scraper_password"]) + + # since MUST BE TIMEZONE AWARE + # usage example: since=datetime(2023, 8, 1).replace(tzinfo=pytz.utc) + def get_tweets_from_user(self, uid: int | str, since: datetime = None) -> list: + reached_backdate = False + tweets: list[Tweet] = [] + + if since == None: + since = datetime.utcnow().replace(tzinfo=pytz.utc) - timedelta(days=7) + print(f'Grabbing tweets since 7 days ago ({since.date()})') + + if isinstance(uid, str): + name = uid + uid = self.app._get_user_id(uid) + print(f"{name} = {uid}") + + def add_tweet(tweet: Tweet): + nonlocal reached_backdate + try: + tweet.author + tweets.append(tweet) + if not reached_backdate and tweet.date <= since: + print("reached backdate") + reached_backdate = True + except AttributeError: + print("skipping malformed tweet: {tweet}") + return + + uts = self.app.get_tweets(uid, replies=True) + while not reached_backdate: + cur_page = uts.tweets + print(f'obtained {len(cur_page)} tweets') + + if len(cur_page) == 0: break + + for e in cur_page: + if isinstance(e, Tweet): + add_tweet(e) + elif isinstance(e, TweetThread): + # FIXME: rework when replied_to is fixed (currently only user_mentions works) + t = e[-1] # latest tweet in thread = og author's reply + t.replied_to = e[-2] + add_tweet(t) + print(f"adding thread latest: {t.id}") + + uts = self.app.get_tweets(uid, replies=True, cursor=uts.cursor) + + tweets.sort(key=lambda t: t.id) + return tweets + + def get_cross_ttweets_from_user(self, uid: int | str, since: datetime = None): + tweets = self.get_tweets_from_user(uid, since) + ret: [TalentTweet] = [] + for t in tweets: + is_niji = is_niji(int(t.author.id)) + is_cross = False + + # cross-rt? + + # rt mentions cross-company? + + # cross-qrt? + + # cross-reply? + if t.replied_to is not None: + if is_niji == is_holo(int(t.replied_to.author.id)): + is_cross = True + + # cross-mention? in-thread? + for u in t.user_mentions: + if is_niji == is_holo(int(u.id)): + is_cross = True + +if __name__ == '__main__': + app = Scraper() + tweets = app.get_tweets_from_user("pomurainpuff") + print_tweets(tweets) \ No newline at end of file diff --git a/src/talent_lists.py b/src/talent_lists.py index 51b945b..c134933 100644 --- a/src/talent_lists.py +++ b/src/talent_lists.py @@ -47,6 +47,9 @@ def is_niji(id: int) -> bool: def is_holo(id: int) -> bool: return id in holo_en or id in holo_id +def is_cross_company(id1: int, id2: int): + return is_niji(id1) == is_holo(id2) + # For filtered stream # DEPRECATED: thx elon def get_twitter_rules(): diff --git a/src/talenttweet.py b/src/talenttweet.py index ab91737..baa2c54 100644 --- a/src/talenttweet.py +++ b/src/talenttweet.py @@ -1,14 +1,33 @@ -import datetime +from datetime import datetime from zoneinfo import ZoneInfo import platform import pytz -import twapi import talent_lists import util class TalentTweet: + # Serialized one-liner format: + # {tweet} {author} {time in seconds since epoch} m {mention set} r {reply to author} q {quote tweet author} rt {retweeted tweet's id} + def serialize(self): + s = f'{self.tweet_id} {self.author_id} {self.date_time.timestamp()} ' + + if None not in [self.rt_target, self.rt_author_id]: + s += f'rt {self.rt_target} {self.rt_author_id}' + return s[:-1] # stop here since retweets can't have other info + + if len(self.mentions) > 0: + s += 'm ' + for id in self.mentions: + s += f'{id} ' + if self.reply_to: + s += f'r {self.reply_to} ' + if self.quote_retweeted: + s += f'q {self.quote_retweeted} ' + + return s[:-1] + @staticmethod def deserialize(serialized_str: str): tokens = serialized_str.split() @@ -16,7 +35,7 @@ class TalentTweet: raise ValueError('not enough tokens to reconstruct a TalentTweet') tweet_id, author_id = int(tokens[0]), int(tokens[1]) - date_time = datetime.datetime.fromtimestamp(float(tokens[2]), tz=pytz.utc) + date_time = datetime.fromtimestamp(float(tokens[2]), tz=pytz.utc) mentions = set() reply_to = None @@ -43,27 +62,7 @@ class TalentTweet: date_time=date_time, mrq=(mentions, reply_to, quote_retweeted) ) - # Serialized one-liner format: - # {tweet} {author} {time in seconds since epoch} m {mention_set} r {reply_to_author} q {quote_retweet_author} - def serialize(self): - s = f'{self.tweet_id} {self.author_id} {self.date_time.timestamp()} ' - - if None not in [self.rt_target, self.rt_author_id]: - s += f'rt {self.rt_target} {self.rt_author_id}' - return s[:-1] # stop here since retweets can't have other info - - if len(self.mentions) > 0: - s += 'm ' - for id in self.mentions: - s += f'{id} ' - if self.reply_to: - s += f'r {self.reply_to} ' - if self.quote_retweeted: - s += f'q {self.quote_retweeted} ' - - return s[:-1] - - def __init__(self, tweet_id: int, author_id: int, date_time: datetime.datetime, mrq: tuple, rt_target: int=None, rt_author_id: int=None): + def __init__(self, tweet_id: int, author_id: int, date_time: datetime, mrq: tuple, rt_target: int=None, rt_author_id: int=None): self.tweet_id, self.author_id = tweet_id, author_id self.date_time = date_time self.mentions = tuple(int(x) for x in mrq[0]) @@ -97,18 +96,8 @@ class TalentTweet: def is_cross_company(self): for other_id in self.all_parties: - if self.author_id in talent_lists.holo_en: - if other_id in talent_lists.niji_en or other_id in talent_lists.niji_exid: - return True - if self.author_id in talent_lists.niji_en: - if other_id in talent_lists.holo_en or other_id in talent_lists.holo_id: - return True - if self.author_id in talent_lists.holo_id: - if other_id in talent_lists.niji_en or other_id in talent_lists.niji_exid: - return True - if self.author_id in talent_lists.niji_exid: - if other_id in talent_lists.holo_en or other_id in talent_lists.holo_id: - return True + if talent_lists.is_cross_company(self.author_id, other_id): + return True return False def get_all_parties_usernames(self): diff --git a/src/twapi.py b/src/twapi.py index e44e5f8..687393d 100644 --- a/src/twapi.py +++ b/src/twapi.py @@ -124,35 +124,7 @@ class TwAPI: # else: # print('Saul Gone') - def get_all_tweet_ids_from_user(self, user_id): - next_page_token = None - tokens_retrieved = 0 - tweets_retrieved = 0 - tweets = list() - while True: - print(f'Retrieved {tokens_retrieved} tokens so far...') - resp = self.client.get_users_tweets( - user_id, max_results=100, pagination_token=next_page_token, - media_fields=TwAPI.TWEET_MEDIA_FIELDS, - tweet_fields=TwAPI.TWEET_FIELDS, - expansions=TwAPI.TWEET_EXPANSIONS - ) - - for tweet in resp.data: - tweets.append(tweet) - - # update counters and pagination token - tweets_retrieved += resp.meta['result_count'] - try: - next_page_token = resp.meta['next_token'] - tokens_retrieved += 1 - except KeyError: - print("next_token wasn't provided; we've reached the end!") - break # reached end of user's tweets - - print(f'Retrieved {tweets_retrieved} tweets using {tokens_retrieved} tokens.') - return tweets - + # DEPRECATED: thx elon async def get_tweet_response(self, id, attempt = 0): try: twt = TwAPI.instance.client.get_tweet( @@ -275,18 +247,3 @@ class TwAPI: else: raise e return True - - def post_ttweet_by_id(self, tweet_id, is_catchup=False, dry_run=False): - ttweet = asyncio.run(tt.TalentTweet.create_from_id(tweet_id)) - print(f'm({ttweet.mentions}), r({ttweet.reply_to}), q({ttweet.quote_retweeted})') - if ttweet.is_cross_company(): - print(f'Tweet {ttweet.tweet_id} is cross-company! Creating post...') - asyncio.run(self.post_ttweet(ttweet, is_catchup=is_catchup, dry_run=dry_run)) - ttq.TalentTweetQueue.instance.add_finished_tweet(ttweet.tweet_id) - else: - print(f'Tweet {tweet_id} is not cross-company.') - - - - - diff --git a/src/tweety_utils.py b/src/tweety_utils.py new file mode 100644 index 0000000..8d96b04 --- /dev/null +++ b/src/tweety_utils.py @@ -0,0 +1,19 @@ +from tweety.types import * + +def url(t: Tweet): + return f'https://twitter.com/{t.author.username}/status/{t.id}' + +def print_tweets(tweets: list[Tweet | TweetThread]): + print(f'{len(tweets)} tweets:') + for t in tweets: + if isinstance(t, Tweet): + print(f'{t.date} : {url(t)} : RT? {t.is_retweet} ', end=' ') + + if t.replied_to is not None: + print(f'reply to {t.replied_to.author.username}', end=' ') + + print("m=" + ",".join([x.username for x in t.user_mentions])) + elif isinstance(t, TweetThread): + print('-----------TTd----------') + print_tweets(t.tweets) + print('-----------end----------') \ No newline at end of file diff --git a/src/util.py b/src/util.py index dc0762f..b23cbb5 100644 --- a/src/util.py +++ b/src/util.py @@ -8,7 +8,7 @@ from datetime import datetime import tweepy import pytz import twint -import twapi +#import twapi from tweetcapture import TweetCapture from recrop import fix_aspect_ratio From 3ffb97a81992b45c161ff49ad280e8bddcb52040 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Tue, 15 Aug 2023 17:30:37 -0700 Subject: [PATCH 07/18] switch over to my tweety fork temporarily --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b705a24..31ac71a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ python-dotenv nest-asyncio pytz -tweety-ns +git+https://github.com/muskit/tweety-ns.git tweepy tweet-capture opencv-python \ No newline at end of file From 5e6d8ce5e19e6ec7826b084c40216f13a6c9316f Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Tue, 15 Aug 2023 17:32:01 -0700 Subject: [PATCH 08/18] fix talent_lists --- src/talent_lists.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/talent_lists.py b/src/talent_lists.py index c134933..6f32a37 100644 --- a/src/talent_lists.py +++ b/src/talent_lists.py @@ -1,11 +1,11 @@ import util -holo_en = dict[int, str] -holo_id = dict[int, str] -niji_en = dict[int, str] -niji_exid = dict[int, str] -talents = dict[int, str] -talents_company = dict[int, str] +holo_en: dict[int, str] = dict() +holo_id: dict[int, str] = dict() +niji_en: dict[int, str] = dict() +niji_exid: dict[int, str] = dict() +talents: dict[int, str] = dict() +talents_company: dict[int, str] = dict() test_talents = dict() @@ -48,7 +48,7 @@ def is_holo(id: int) -> bool: return id in holo_en or id in holo_id def is_cross_company(id1: int, id2: int): - return is_niji(id1) == is_holo(id2) + return (is_niji(id1) and is_holo(id2)) or (is_holo(id1) and is_niji(id2)) # For filtered stream # DEPRECATED: thx elon From 11617647694468b1d591806337393856f990157e Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Tue, 15 Aug 2023 17:33:29 -0700 Subject: [PATCH 09/18] tweety scraper implementation progress --- src/scraper.py | 112 ++++++++++++++++++++++---------------------- src/talenttweet.py | 52 +++++++++++++++----- src/twapi.py | 2 +- src/tweety_utils.py | 7 ++- src/util.py | 22 ++++----- 5 files changed, 114 insertions(+), 81 deletions(-) diff --git a/src/scraper.py b/src/scraper.py index c20e6e5..9e77dc9 100644 --- a/src/scraper.py +++ b/src/scraper.py @@ -7,93 +7,95 @@ import pytz from tweety import Twitter from tweety.types import * +from tweety.exceptions_ import * +from tweety.filters import SearchFilters from tweety_utils import * from talenttweet import * -from talent_lists import is_niji, is_holo +import talent_lists class Scraper: def __init__(self): creds = dotenv_values() self.app = Twitter("session") - if exists("session.json"): - self.app.connect() - else: - self.app.sign_in(creds["scraper_username"], creds["scraper_password"]) + # if exists("session.json"): + # self.app.connect() + # else: + self.app.sign_in(creds["scraper_username"], creds["scraper_password"]) # since MUST BE TIMEZONE AWARE # usage example: since=datetime(2023, 8, 1).replace(tzinfo=pytz.utc) - def get_tweets_from_user(self, uid: int | str, since: datetime = None) -> list: + def get_tweets_from_user(self, username: str, since: datetime = None) -> list[Tweet]: reached_backdate = False tweets: list[Tweet] = [] + cur = None if since == None: since = datetime.utcnow().replace(tzinfo=pytz.utc) - timedelta(days=7) - print(f'Grabbing tweets since 7 days ago ({since.date()})') + print(f'falling back to grabbing tweets since 7 days ago ({since.date()})') + else: + print(f'grabbing tweets since {since.date()}') - if isinstance(uid, str): - name = uid - uid = self.app._get_user_id(uid) - print(f"{name} = {uid}") + uid = self.app._get_user_id(username) + print(f"{username} = {uid}") def add_tweet(tweet: Tweet): + # malformed tweet check nonlocal reached_backdate try: tweet.author - tweets.append(tweet) - if not reached_backdate and tweet.date <= since: - print("reached backdate") - reached_backdate = True except AttributeError: print("skipping malformed tweet: {tweet}") return - uts = self.app.get_tweets(uid, replies=True) + # fix reply if it exists + # if tweet.is_reply and tweet.replied_to is None: + # tweet.replied_to = self.app.tweet_detail(tweet._original_tweet['in_reply_to_status_id_str']) + tweets.append(tweet) + + if not reached_backdate and int(tweet.author.id) == uid and tweet.date <= since: + print("reached backdate") + reached_backdate = True + while not reached_backdate: - cur_page = uts.tweets - print(f'obtained {len(cur_page)} tweets') + try: + # uts = self.app.get_tweets(uid, replies=True, cursor=cur) + search = self.app.search(f'from:{username}', filter_=SearchFilters.Latest(), cursor=cur) + cur_page = search.tweets + print(f'obtained {len(cur_page)} tweets') - if len(cur_page) == 0: break + if len(cur_page) == 0: break - for e in cur_page: - if isinstance(e, Tweet): - add_tweet(e) - elif isinstance(e, TweetThread): - # FIXME: rework when replied_to is fixed (currently only user_mentions works) - t = e[-1] # latest tweet in thread = og author's reply - t.replied_to = e[-2] - add_tweet(t) - print(f"adding thread latest: {t.id}") - - uts = self.app.get_tweets(uid, replies=True, cursor=uts.cursor) + for e in cur_page: + if isinstance(e, Tweet): + add_tweet(e) + elif isinstance(e, TweetThread): + # FIXME: rework when replied_to is fixed (currently populates user_mentions) + # latest tweet in thread = og author's reply + add_tweet(e[0]) + for t in e: + add_tweet(t) + + cur = search.cursor + except UnknownError: + print("UnknownError occurred, probably rate-limited") + print("sleeping for 1 minute...") + sleep(60) tweets.sort(key=lambda t: t.id) return tweets - def get_cross_ttweets_from_user(self, uid: int | str, since: datetime = None): - tweets = self.get_tweets_from_user(uid, since) - ret: [TalentTweet] = [] + def get_cross_ttweets_from_user(self, username: str, since: datetime = None) -> list[TalentTweet]: + tweets = self.get_tweets_from_user(username, since) + print_tweets(tweets) + ret: list[TalentTweet] = [] for t in tweets: - is_niji = is_niji(int(t.author.id)) - is_cross = False + tt = TalentTweet.create_from_tweety(t) + if tt.is_cross_company(): + ret.append(tt) + return ret - # cross-rt? - - # rt mentions cross-company? - - # cross-qrt? - - # cross-reply? - if t.replied_to is not None: - if is_niji == is_holo(int(t.replied_to.author.id)): - is_cross = True - - # cross-mention? in-thread? - for u in t.user_mentions: - if is_niji == is_holo(int(u.id)): - is_cross = True - -if __name__ == '__main__': - app = Scraper() - tweets = app.get_tweets_from_user("pomurainpuff") - print_tweets(tweets) \ No newline at end of file +talent_lists.init() +s = Scraper() +ttweets = s.get_cross_ttweets_from_user("pomurainpuff", since=datetime(2023, 7, 30).replace(tzinfo=pytz.utc)) +print("\n".join([x.__repr__() for x in ttweets])) \ No newline at end of file diff --git a/src/talenttweet.py b/src/talenttweet.py index baa2c54..a515fcf 100644 --- a/src/talenttweet.py +++ b/src/talenttweet.py @@ -3,8 +3,9 @@ from zoneinfo import ZoneInfo import platform import pytz +from tweety.types import * -import talent_lists +from talent_lists import is_cross_company import util class TalentTweet: @@ -13,8 +14,8 @@ class TalentTweet: def serialize(self): s = f'{self.tweet_id} {self.author_id} {self.date_time.timestamp()} ' - if None not in [self.rt_target, self.rt_author_id]: - s += f'rt {self.rt_target} {self.rt_author_id}' + if self.rt_author_id != None: + s += f'rt {self.rt_id} {self.rt_author_id}' return s[:-1] # stop here since retweets can't have other info if len(self.mentions) > 0: @@ -61,14 +62,35 @@ class TalentTweet: tweet_id=tweet_id, author_id=author_id, date_time=date_time, mrq=(mentions, reply_to, quote_retweeted) ) + + ## Creates a TalentTweet from a Tweety-library Tweet. + @staticmethod + def create_from_tweety(tweety: Tweet): + return TalentTweet( + tweet_id=int(tweety.id), author_id=int(tweety.author.id), + date_time=tweety.date, text=tweety.text, + mrq=( + [int(x.id) for x in tweety.user_mentions], + int(tweety._original_tweet['in_reply_to_user_id_str']) if tweety.is_reply else None, + int(tweety.quoted_tweet.author.id) if tweety.quoted_tweet is not None else None + ), + rt_author_id=tweety.retweeted_tweet.author.id if tweety.is_retweet else None, + rt_mentions=[int(x.id) for x in tweety.retweeted_tweet.user_mentions] if tweety.is_retweet else list() + ) - def __init__(self, tweet_id: int, author_id: int, date_time: datetime, mrq: tuple, rt_target: int=None, rt_author_id: int=None): + def __init__(self, tweet_id: int, author_id: int, date_time: datetime, text: str = None, mrq: tuple[list[int], int|None, int|None]=None, rt_author_id: int=None, rt_mentions: list[int]=None): + # basic information self.tweet_id, self.author_id = tweet_id, author_id + self.username = util.get_username_local(self.author_id) self.date_time = date_time - self.mentions = tuple(int(x) for x in mrq[0]) - self.reply_to = int(mrq[1]) if mrq[1] is not None else None - self.quote_retweeted = int(mrq[2]) if mrq[2] is not None else None - self.rt_target, self.rt_author_id = rt_target, rt_author_id + self.text = text + + # filter twitter users to only be cross-company + self.mentions = {x for x in mrq[0] if is_cross_company(author_id, x)} + self.reply_to = mrq[1] if mrq[1] is not None and is_cross_company(author_id, mrq[1]) else None + self.quote_retweeted = mrq[2] if mrq[2] is not None and is_cross_company(author_id, mrq[2]) else None + self.rt_mentions = {x for x in rt_mentions if is_cross_company(author_id, x)} if rt_mentions is not None else None + self.rt_author_id = rt_author_id if (rt_author_id is not None and is_cross_company(author_id, rt_author_id)) or (len(self.rt_mentions) > 0) else None # all users involved, except for the author self.all_parties = {self.reply_to, self.quote_retweeted} @@ -83,20 +105,24 @@ class TalentTweet: def __repr__(self) -> str: return ( - f'{self.tweet_id} from {util.get_username_local(self.author_id)}):\n' + f'======================================================' + f'{self.tweet_id} from {self.username}:\n' f'{self.get_datetime_str()}\n' - f'{self.get_all_parties_usernames()}\n' + f'parties: {self.get_all_parties_usernames()}\n' f'mentions: {self.mentions}\n' f'reply_to: {self.reply_to}\n' f'quote_retweeted: {self.quote_retweeted}\n' - f'Cross-company: {self.is_cross_company()}\n' + f'cross-company? {self.is_cross_company()}\n' f'{self.serialize()}\n' - f'======================================================' + f'{self.url()}' ) + def url(self): + return f'https://www.twitter.com/{self.username}/status/{self.tweet_id}' + def is_cross_company(self): for other_id in self.all_parties: - if talent_lists.is_cross_company(self.author_id, other_id): + if is_cross_company(self.author_id, other_id): return True return False diff --git a/src/twapi.py b/src/twapi.py index 687393d..f377e6f 100644 --- a/src/twapi.py +++ b/src/twapi.py @@ -175,7 +175,7 @@ class TwAPI: pass # Tweet types - if ttweet.rt_target is not None: # retweet + if ttweet.rt_id is not None: # retweet ret += RETWEET.format(f'{author_username}', f'@/{util.get_username_with_company(ttweet.rt_author_id)}') elif ttweet.reply_to is not None: # reply reply_username = f'@/{util.get_username_with_company(ttweet.reply_to)}' diff --git a/src/tweety_utils.py b/src/tweety_utils.py index 8d96b04..ad21e05 100644 --- a/src/tweety_utils.py +++ b/src/tweety_utils.py @@ -7,8 +7,13 @@ def print_tweets(tweets: list[Tweet | TweetThread]): print(f'{len(tweets)} tweets:') for t in tweets: if isinstance(t, Tweet): - print(f'{t.date} : {url(t)} : RT? {t.is_retweet} ', end=' ') + print(f'{t.date} : {url(t)} :', end=' ') + if t.is_retweet: + print(f'RT ({t.retweeted_tweet.author.username})', end=' ') + + if t.is_reply: + print(f'is reply!', end=' ') if t.replied_to is not None: print(f'reply to {t.replied_to.author.username}', end=' ') diff --git a/src/util.py b/src/util.py index b23cbb5..5f9148a 100644 --- a/src/util.py +++ b/src/util.py @@ -101,20 +101,20 @@ def ttweet_to_url(ttweet): # except: # return str(default) if default is not None else f'{id}' -def get_username_local(id): +def get_username_local(id: int): return talent_lists.talents.get(id, f'{id}') # Retrieve username via API v2 (tweepy) -def get_username_online(id, default=None): - try: - resp = twapi.TwAPI.instance.client.get_user(id=id) - return resp.data.username - except tweepy.TooManyRequests: - return str(default) if default is not None else f'id:{id}' - except: - print(f'Unhandled error retrieving username for {id}!') - traceback.print_exc() - return str(default) if default is not None else f'id:{id}' +# def get_username_online(id, default=None): +# try: +# resp = twapi.TwAPI.instance.client.get_user(id=id) +# return resp.data.username +# except tweepy.TooManyRequests: +# return str(default) if default is not None else f'id:{id}' +# except: +# print(f'Unhandled error retrieving username for {id}!') +# traceback.print_exc() +# return str(default) if default is not None else f'id:{id}' ## Attempt to pull username from local; pull from online if doesn't exist. def get_username(id): From 41b261072f8f6f57bf568a1cd40f8b9d19c68d20 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Wed, 16 Aug 2023 18:48:13 -0700 Subject: [PATCH 10/18] TalentTweet work, cleanups - added other RT/QRT considerations - announce tweet text moved to TalentTweet - some general clean up --- retweet notes.txt | 16 ++++++ src/talenttweet.py | 118 +++++++++++++++++++++++++++++++++++++-------- src/twapi.py | 66 ++----------------------- src/util.py | 45 +++++------------ 4 files changed, 129 insertions(+), 116 deletions(-) create mode 100644 retweet notes.txt diff --git a/retweet notes.txt b/retweet notes.txt new file mode 100644 index 0000000..04809b4 --- /dev/null +++ b/retweet notes.txt @@ -0,0 +1,16 @@ +possible combinations which involve a "target cross-tweeter" B + +A retweets B + - B's tweet may have cross-mentions (B1, B2, etc.) + - rt_author_id=B; rt_mentions=B1,B2,... +A retweets tweet mentioning B + - rt_author_id=...; rt_mentions=B... + +A quotes a tweet from B + - B's tweet may have cross-mentions (B1, B2, etc.) + - quote_retweeted=B; rt_mentions=B1,B2,... +A quotes a tweet mentioning B + - quote_retweeted=...; rt_mentions=B... + +-- NO -- +A retweets a tweet that quotes a tweet mentioning B? diff --git a/src/talenttweet.py b/src/talenttweet.py index a515fcf..969d961 100644 --- a/src/talenttweet.py +++ b/src/talenttweet.py @@ -10,12 +10,19 @@ import util class TalentTweet: # Serialized one-liner format: - # {tweet} {author} {time in seconds since epoch} m {mention set} r {reply to author} q {quote tweet author} rt {retweeted tweet's id} + # {tweet} {author} {time in seconds since epoch UTC} m {mention set} r {reply to author} q {quote tweet author} rt {retweeted user's id} rtm {mentions in retweet} def serialize(self): - s = f'{self.tweet_id} {self.author_id} {self.date_time.timestamp()} ' + s = f'{self.tweet_id} {self.author_id} {int(self.date_time.timestamp())} ' + if self.date_time.tzinfo is None: + print(f'warning: serialized tweet {self.tweet_id} has a NAIVE timestamp!') + + if len(self.rt_mentions) > 0: + s += 'rtm ' + for n in self.rt_mentions: + s += f'{n} ' if self.rt_author_id != None: - s += f'rt {self.rt_id} {self.rt_author_id}' + s += f'rt {self.rt_author_id} ' return s[:-1] # stop here since retweets can't have other info if len(self.mentions) > 0: @@ -24,23 +31,25 @@ class TalentTweet: s += f'{id} ' if self.reply_to: s += f'r {self.reply_to} ' - if self.quote_retweeted: - s += f'q {self.quote_retweeted} ' + if self.quote_tweeted: + s += f'q {self.quote_tweeted} ' return s[:-1] @staticmethod def deserialize(serialized_str: str): - tokens = serialized_str.split() + tokens = serialized_str.split('#')[0] if len(tokens) < 3: raise ValueError('not enough tokens to reconstruct a TalentTweet') tweet_id, author_id = int(tokens[0]), int(tokens[1]) date_time = datetime.fromtimestamp(float(tokens[2]), tz=pytz.utc) - mentions = set() + mentions = list() reply_to = None quote_retweeted = None + rt = None + rtm = list() mode = '' for i in range(3, len(tokens)): @@ -57,15 +66,27 @@ class TalentTweet: continue if mode == 'q': # quote_retweeted quote_retweeted = int(tokens[i]) + if mode == 'rt': # retweeted user + rt = int(tokens[i]) + if mode == 'rtm': # retweet/qrt mentions + rtm = int(tokens[i]) return TalentTweet( tweet_id=tweet_id, author_id=author_id, - date_time=date_time, mrq=(mentions, reply_to, quote_retweeted) + date_time=date_time, mrq=(mentions, reply_to, quote_retweeted), + rt_author_id=rt, rt_mentions=rtm ) ## Creates a TalentTweet from a Tweety-library Tweet. @staticmethod def create_from_tweety(tweety: Tweet): + if tweety.is_retweet: + rtm = [int(x.id) for x in tweety.retweeted_tweet.user_mentions] + elif tweety.is_quoted: + rtm = [int(x.id) for x in tweety.quoted_tweet.user_mentions] + else: + rtm = list() + return TalentTweet( tweet_id=int(tweety.id), author_id=int(tweety.author.id), date_time=tweety.date, text=tweety.text, @@ -75,7 +96,7 @@ class TalentTweet: int(tweety.quoted_tweet.author.id) if tweety.quoted_tweet is not None else None ), rt_author_id=tweety.retweeted_tweet.author.id if tweety.is_retweet else None, - rt_mentions=[int(x.id) for x in tweety.retweeted_tweet.user_mentions] if tweety.is_retweet else list() + rt_mentions=rtm ) def __init__(self, tweet_id: int, author_id: int, date_time: datetime, text: str = None, mrq: tuple[list[int], int|None, int|None]=None, rt_author_id: int=None, rt_mentions: list[int]=None): @@ -88,32 +109,37 @@ class TalentTweet: # filter twitter users to only be cross-company self.mentions = {x for x in mrq[0] if is_cross_company(author_id, x)} self.reply_to = mrq[1] if mrq[1] is not None and is_cross_company(author_id, mrq[1]) else None - self.quote_retweeted = mrq[2] if mrq[2] is not None and is_cross_company(author_id, mrq[2]) else None - self.rt_mentions = {x for x in rt_mentions if is_cross_company(author_id, x)} if rt_mentions is not None else None - self.rt_author_id = rt_author_id if (rt_author_id is not None and is_cross_company(author_id, rt_author_id)) or (len(self.rt_mentions) > 0) else None + self.quote_tweeted = mrq[2] - # all users involved, except for the author - self.all_parties = {self.reply_to, self.quote_retweeted} - self.all_parties.update(self.mentions) - try: - self.all_parties.remove(None) + # rt'd/quoted tweet contains cross-company names? + self.rt_mentions = {x for x in rt_mentions if is_cross_company(author_id, x)} + self.rt_author_id = rt_author_id + + # all users involved except for the author + self.all_parties = {self.reply_to, self.quote_tweeted, rt_author_id} + self.all_parties.update(self.mentions, self.rt_mentions) + try: self.all_parties.remove(None) except: pass - try: - self.all_parties.remove(self.author_id) + try: self.all_parties.remove(self.author_id) + except: pass + + # clean up mentions + try: self.mentions.remove(self.reply_to) except: pass def __repr__(self) -> str: return ( - f'======================================================' + f'======================================================\n' f'{self.tweet_id} from {self.username}:\n' f'{self.get_datetime_str()}\n' f'parties: {self.get_all_parties_usernames()}\n' f'mentions: {self.mentions}\n' f'reply_to: {self.reply_to}\n' - f'quote_retweeted: {self.quote_retweeted}\n' + f'quote_retweeted: {self.quote_tweeted}\n' f'cross-company? {self.is_cross_company()}\n' f'{self.serialize()}\n' + f'----\n{self.announce_text()}\n----\n' f'{self.url()}' ) @@ -138,3 +164,53 @@ class TalentTweet: def get_datetime_str(self): unpad = '#' if platform.system() == 'Windows' else '-' return self.date_time.strftime(f'%b %{unpad}d %Y, %{unpad}I:%M%p (%Z)') + + def announce_text(self, is_catchup=False): + # templates + REPLY = '{0} replied to {1}!' + TWEET = '{0} tweeted!' + RETWEET = '{0} retweeted {1}!' + RETWEET_MENTIONS_B = '{0} shared a tweet mentioning{1}!' + QUOTE_TWEET = '{0} quote tweeted {1}!' + QUOTE_TWEET_MENTIONS_B = '{0} quoted a tweet mentioning {1}!' + + author_username = f'@/{util.get_username_with_company(self.author_id)}' + ret = str() + print_mention_ids = set(self.mentions) + if is_catchup: + ret += f'{self.get_datetime_str()}\n' + pass + + rt_mention_names = [util.get_username_with_company(x) for x in self.rt_mentions] + # Tweet types + if self.rt_author_id is not None: # retweet + if len(self.rt_mentions) > 0: + ret += RETWEET_MENTIONS_B.format(author_username, ", ".join(rt_mention_names)) + else: + ret += RETWEET.format(f'{author_username}', f'@/{util.get_username_with_company(self.rt_author_id)}') + elif self.reply_to is not None: # reply + reply_username = f'@/{util.get_username_with_company(self.reply_to)}' + ret += REPLY.format(author_username, reply_username) + elif self.quote_tweeted is not None: # qrt + quoted_username = f'@/{util.get_username_with_company(self.quote_tweeted)}' + if len(self.rt_mentions) > 0: + ret += QUOTE_TWEET_MENTIONS_B.format(author_username, ", ".join(rt_mention_names)) + else: + ret += QUOTE_TWEET.format(author_username, quoted_username) + elif len(self.mentions) > 0: # standalone tweet + ret += TWEET.format(author_username) + else: + raise ValueError(f'TalentTweet {self.tweet_id} has insufficient other parties') + + try: print_mention_ids.remove(None) + except: pass + + # mention line + if len(print_mention_ids) > 0: + mention_usernames = [f'@/{util.get_username_with_company(x)}' for x in print_mention_ids] + ret += ( + '\nMentioning ' + f'{", ".join(mention_usernames)}' + ) + + return ret diff --git a/src/twapi.py b/src/twapi.py index f377e6f..c7f9e6f 100644 --- a/src/twapi.py +++ b/src/twapi.py @@ -124,23 +124,6 @@ class TwAPI: # else: # print('Saul Gone') - # DEPRECATED: thx elon - async def get_tweet_response(self, id, attempt = 0): - try: - twt = TwAPI.instance.client.get_tweet( - id, - media_fields=TwAPI.TWEET_MEDIA_FIELDS, - tweet_fields=TwAPI.TWEET_FIELDS, - expansions=TwAPI.TWEET_EXPANSIONS - ) - TwAPI.tweets_fetched += 1 - return twt - except tweepy.TooManyRequests as e: - wait_for = float(e.response.headers["x-rate-limit-reset"]) - datetime.datetime.now().timestamp() + 1 - print(f'[{attempt}]\tget_tweet_response({id}):\n\thit rate limit after {TwAPI.tweets_fetched} fetches -- trying again in {wait_for} seconds...') - await asyncio.sleep(wait_for) - return await self.get_tweet_response(id, attempt=attempt+1) - async def post_tweet(self, text='', media_ids: list=None, reply_to_tweet: int=None, quote_tweet_id: int=None): try: tweet = self.client.create_tweet(text=text, media_ids=media_ids, in_reply_to_tweet_id=reply_to_tweet, quote_tweet_id=quote_tweet_id) @@ -160,55 +143,12 @@ class TwAPI: # return False = did not post ttweet (duplicate) async def post_ttweet(self, ttweet: tt.TalentTweet, is_catchup=False, dry_run=False): print(f'------{ttweet.tweet_id} ({util.get_username_local(ttweet.author_id)})------') - - REPLY = '{0} replied to {1}!\n' - QUOTE_TWEET = '{0} quote tweeted {1}!\n' - TWEET = '{0} tweeted!\n' - RETWEET = '{0} retweeted {1}!\n' - - def create_text(): - author_username = f'@/{util.get_username_with_company(ttweet.author_id)}' - print_mention_ids = set(ttweet.mentions) - ret = str() - if is_catchup: - ret += f'{ttweet.get_datetime_str()}\n' - pass - - # Tweet types - if ttweet.rt_id is not None: # retweet - ret += RETWEET.format(f'{author_username}', f'@/{util.get_username_with_company(ttweet.rt_author_id)}') - elif ttweet.reply_to is not None: # reply - reply_username = f'@/{util.get_username_with_company(ttweet.reply_to)}' - ret += REPLY.format(author_username, reply_username) - # if qrt, push id into mentions - print_mention_ids.add(ttweet.quote_retweeted) - elif ttweet.quote_retweeted is not None: # qrt - quoted_username = f'@/{util.get_username_with_company(ttweet.quote_retweeted)}' - ret += QUOTE_TWEET.format(author_username, quoted_username) - elif len(ttweet.mentions) > 0: # standalone tweet - ret += TWEET.format(author_username) - else: - raise ValueError(f'TalentTweet {ttweet.tweet_id} has insufficient other parties') - - try: print_mention_ids.remove(None) - except: pass - - # mention line - if len(print_mention_ids) > 0: - mention_usernames = [f'@/{util.get_username_with_company(x)}' for x in print_mention_ids] - ret += ( - 'mentioning ' - f'{", ".join(mention_usernames)}\n' - ) - ret += '\n' - # ret += '(this is a missed tweet)\n' if is_catchup else '' - return ret - text = create_text() - ttweet_url = util.ttweet_to_url(ttweet) + text = ttweet.announce_text() + ttweet_url = ttweet.url() if dry_run: print('-------------------- DRY RUN --------------------') - print(text) + print(ttweet) if dry_run: return False # NO DRY-RUN: actually post tweet diff --git a/src/util.py b/src/util.py index 5f9148a..289c273 100644 --- a/src/util.py +++ b/src/util.py @@ -45,7 +45,7 @@ def get_current_timestamp(): def get_current_date(): return datetime.today().strftime('%Y-%m-%d') -def get_key_from_value(d, val): +def get_key_from_value(d: dict, val): keys = [k for k, v in d.items() if v == val] if keys: return keys[0] @@ -101,21 +101,6 @@ def ttweet_to_url(ttweet): # except: # return str(default) if default is not None else f'{id}' -def get_username_local(id: int): - return talent_lists.talents.get(id, f'{id}') - -# Retrieve username via API v2 (tweepy) -# def get_username_online(id, default=None): -# try: -# resp = twapi.TwAPI.instance.client.get_user(id=id) -# return resp.data.username -# except tweepy.TooManyRequests: -# return str(default) if default is not None else f'id:{id}' -# except: -# print(f'Unhandled error retrieving username for {id}!') -# traceback.print_exc() -# return str(default) if default is not None else f'id:{id}' - ## Attempt to pull username from local; pull from online if doesn't exist. def get_username(id): ret = talent_lists.talents.get(id, None) @@ -127,21 +112,17 @@ def get_username_with_company(id): company = talent_lists.talents_company.get(id, None) return f'{get_username(id)} {f"({company})" if company is not None else ""}' -def get_user_id_local(username) -> int: - talent_usernames = list(talent_lists.talents.values()) - for i in range(0, len(talent_usernames)): - if username.lower() == talent_usernames[i].lower(): - return list(talent_lists.talents)[i] - -def get_user_id_online(username) -> int: - c = twint.Config() - c.Username = username - c.Store_object = True - c.Hide_output = True +def get_username_local(id: int): + return talent_lists.talents.get(id, f'{id}') + +# Retrieve username via API v2 (tweepy) +def get_username_online(id, default=None): try: - twint.output.users_list.clear() - twint.run.Lookup(c) - user = twint.output.users_list[0] - return user.id + resp = twapi.TwAPI.instance.client.get_user(id=id) + return resp.data.username + except tweepy.TooManyRequests: + return str(default) if default is not None else f'id:{id}' except: - return -1 + print(f'Unhandled error retrieving username for {id}!') + traceback.print_exc() + return str(default) if default is not None else f'id:{id}' \ No newline at end of file From dd4578708aba5aab941fe49942a05c618bb231ef Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:18:56 -0700 Subject: [PATCH 11/18] fix requirements.txt typo --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 31ac71a..72ed794 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ python-dotenv nest-asyncio pytz -git+https://github.com/muskit/tweety-ns.git +git+https://github.com/muskit/tweety.git tweepy tweet-capture opencv-python \ No newline at end of file From 3c93ca18c2af4aaff1ce8eb6e5930b94c1e52772 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Thu, 17 Aug 2023 02:28:29 -0700 Subject: [PATCH 12/18] (de)serialization and queuing works now --- .gitignore | 2 +- src/account_pool.py | 31 ++++++++++++++++++ src/api_secrets.py | 42 ------------------------ src/catchup.py | 60 ++++++++++++---------------------- src/listen.py | 55 +++---------------------------- src/main.py | 10 +++--- src/scraper.py | 79 ++++++++++++++++++++++++++++++++++----------- src/talenttweet.py | 26 ++++++++------- src/ttweetqueue.py | 6 +++- src/twapi.py | 40 +++++++++++------------ 10 files changed, 163 insertions(+), 188 deletions(-) create mode 100644 src/account_pool.py delete mode 100644 src/api_secrets.py diff --git a/.gitignore b/.gitignore index ac961e8..a0e4b05 100644 --- a/.gitignore +++ b/.gitignore @@ -144,4 +144,4 @@ cython_debug/ # project-specific (secret.ini: can't ignore existing file?) *.png -session.json \ No newline at end of file +*.json \ No newline at end of file diff --git a/src/account_pool.py b/src/account_pool.py new file mode 100644 index 0000000..1a655d7 --- /dev/null +++ b/src/account_pool.py @@ -0,0 +1,31 @@ +from dotenv import dotenv_values + +## Track multiple accounts in a pool, cycling to the next one when requested. +class AccountPool: + def __init__(self): + self.__accounts: list[tuple[str, str]] = list() + self.__idx = -1 + creds = dotenv_values() + i = 0 + while True: + if f'scraper_username{i}' in creds \ + and f'scraper_password{i}' in creds: + self.__accounts.append(( + creds[f'scraper_username{i}'], + creds[f'scraper_password{i}'] + )) + i += 1 + else: + break + + def current(self): + if 0 <= self.__idx < len(self.__accounts): + return self.__accounts[self.__idx] + return None + + def next(self) -> tuple[str, str] | None: + self.__idx += 1 + if self.__idx >= len(self.__accounts): + self.__idx = -1 + return None + return self.current() diff --git a/src/api_secrets.py b/src/api_secrets.py deleted file mode 100644 index ca13bda..0000000 --- a/src/api_secrets.py +++ /dev/null @@ -1,42 +0,0 @@ -## Twitter developer credentials management. - -from os.path import join, isfile -from dotenv import dotenv_values - -import util - -# returns dictionary of the Credentials section. -# [NOT TO BE USED OUTSIDE OF THIS FILE.] -def __get_env(): - f = join(util.get_project_dir(), '.env') - if isfile(f): - return dotenv_values(f) - return None - -# returns the consumer api_key stored in secrets.ini -def api_key(): - c = __get_env() - return c.get(option='api_key', fallback='xxx') if c is not None else 'xxx' - -# returns the consumer api_secret stored in secrets.ini -def api_secret(): - c = __get_env() - return c.get(option='api_secret', fallback='yyy') if c is not None else 'yyy' - -# returns the bearer_token stored in secrets.ini -def bearer_token(): - c = __get_env() - return c.get(option='bearer_token', fallback='zzz') if c is not None else 'zzz' - -# returns the access_token stroed in secrets.ini -def access_token(): - c = __get_env() - return c.get(option='oauth1_access_token', fallback='zzz') if c is not None else 'aaa' - -# returns the access_secret stroed in secrets.ini -def access_secret(): - c = __get_env() - return c.get(option='oauth1_access_secret', fallback='zzz') if c is not None else 'bbb' - -def get_all_secrets(): - return f'api_key:{api_key()}\napi_secret:{api_secret()}\nbearer_token:{bearer_token()}\naccess_token:{access_token()}\naccess_secret:{access_secret()}' \ No newline at end of file diff --git a/src/catchup.py b/src/catchup.py index 759cf22..ffa638b 100644 --- a/src/catchup.py +++ b/src/catchup.py @@ -8,9 +8,9 @@ import traceback import datetime import asyncio import shutil +from datetime import datetime -import twint - +from scraper import Scraper from util import * from talent_lists import * from twapi import TwAPI @@ -21,41 +21,12 @@ PROGRAM_ARGS = None safe_to_post_tweets = False errored = False -## Returns the ID of all tweets (up to limit) from a user ID. -def get_user_tweets(id, since_date=None, limit=None): - global safe_to_post_tweets - - qrt_count = 0 - tweets = list() - c = twint.Config() - c.User_id = id - c.Limit = limit - c.Store_object = True - c.Store_object_tweets_list = tweets - c.Hide_output = True - c.Since = '' if since_date == None else f'{since_date} 00:00:00' - - user_str = f'@{util.get_username_local(id)}' - print(f'Scraping tweets from {user_str} since {"forever ago" if c.Since == "" else c.Since}...') - try: - twint.run.Search(c) - except: - print(f'Had trouble getting tweets from {user_str}') - safe_to_post_tweets = False - traceback.print_exc() - - for twt in tweets: - if type(twt.quote_url) is str and twt.quote_url != '': - qrt_count += 1 - - print(f'Scraped {len(tweets)} tweets, {qrt_count} of which are quote tweets.') - return tweets - # Returns a list of sorted and filtered TalentTweets (should # be equivalent to queue.txt) async def get_cross_talent_tweets(): global safe_to_post_tweets + scraper = Scraper() queue = ttq.TalentTweetQueue.instance # Begin getting tweets from online @@ -64,19 +35,28 @@ async def get_cross_talent_tweets(): for i, (talent_id, talent_username) in enumerate(talent_lists.talents.items()): print(f'[{i+1}/{len(talent_lists.talents)}] {talent_username}-----------------------------------') try: - tweets = get_user_tweets(talent_id, since_date=queue.finished_user_dates.get(talent_id, None)) - for tweet in tweets: - if tweet.id not in queue.ttweets_dict and tweet.id not in queue.finished_ttweets: - ttweet = await tt.TalentTweet.create_from_twint_tweet(tweet) - if ttweet.is_cross_company(): - queue.add_ttweet(ttweet) + # tweets = get_user_tweets(talent_id, since_date=queue.finished_user_dates.get(talent_id, None)) + since_date = queue.finished_user_dates.get(talent_id, None) + ttweets = scraper.get_cross_ttweets_from_user(talent_username, since_date=since_date) + for ttweet in ttweets: + if ttweet.tweet_id not in queue.ttweets_dict \ + and ttweet.tweet_id not in queue.finished_ttweets \ + and ttweet.is_cross_company(): + queue.add_ttweet(ttweet) + except KeyboardInterrupt as e: + raise e except: print('Error occurred processing tweet data.') safe_to_post_tweets = False - print(traceback.format_exc()) - queue.finished_user_dates[talent_id] = '2000-01-01' + traceback.print_exc() + if talent_id not in queue.finished_user_dates: + queue.finished_user_dates[talent_id] = '2023-04-26' # date is when bot token first got revoked else: queue.finished_user_dates[talent_id] = util.get_current_date() + queue.save_file() + except KeyboardInterrupt: + print('Interrupting tweet pulling... NOTE: remaining dates in queue file will not be updated!') + queue.save_file() except: print('Unhandled error occurred while pulling tweets.') traceback.print_exc() diff --git a/src/listen.py b/src/listen.py index 58273d0..e1ccfd1 100644 --- a/src/listen.py +++ b/src/listen.py @@ -1,66 +1,21 @@ ## The bot's listen mode # Continuously listen for cross-company interactions. +from time import sleep import asyncio import traceback -import tweepy -from talenttweet import TalentTweet -from twapi import TwAPI -import ttweetqueue as ttq -import api_secrets -import talent_lists as tl -import util +import catchup errors_encountered = 0 -def on_response(resp): - ttweet = TalentTweet.create_from_v2api_response(resp) - if ttweet is None: - print('Couldn\'t create ttweet from the response:') - print(resp) - return - - tweet_username = util.get_username(ttweet.author_id) - - if ttweet.is_cross_company(): - print(f'Tweet {ttweet.tweet_id} is cross-company! Creating post...') - is_successful = asyncio.run(TwAPI.instance.post_ttweet(ttweet)) - if is_successful: - ttq.TalentTweetQueue.instance.add_finished_tweet(ttweet.tweet_id) - else: - print(f'[WARNING] Failed to post ttweet for {tweet_username}/{ttweet.tweet_id}!') - else: - print(f'Tweet {tweet_username}/{ttweet.tweet_id} is not cross-company.') - def run(): global errors_encountered while True: try: - sc = tweepy.StreamingClient(api_secrets.bearer_token()) - - # clear rules - print('Clearing streaming rules...') - rules_resp = sc.get_rules() - if rules_resp.data: - print('Deleted a rule!') - sc.delete_rules(rules_resp.data) - - # create new rules - print('Creating new streaming rules...') - for rule in tl.get_twitter_rules(): - sc.add_rules(tweepy.StreamRule(rule)) - print('--------------------------------------------') - print(sc.get_rules().data) - print('--------------------------------------------') - - sc.on_response=on_response - print('Starting listening stream...') - sc.filter( - expansions=TwAPI.TWEET_EXPANSIONS, - media_fields=TwAPI.TWEET_MEDIA_FIELDS, - tweet_fields=TwAPI.TWEET_FIELDS - ) + asyncio.run(catchup.run()) + print('Sleeping for 30 minutes...') + sleep(1800) # run every half-hour except KeyboardInterrupt: print('Interrupt signal received. Exiting listen mode.') print(f'{errors_encountered} errors encountered throughout session.') diff --git a/src/main.py b/src/main.py index 01eba94..ed60f60 100644 --- a/src/main.py +++ b/src/main.py @@ -8,7 +8,6 @@ import nest_asyncio import talent_lists import ttweetqueue as ttq -import api_secrets import catchup import listen from twapi import TwAPI @@ -52,6 +51,8 @@ async def async_main(): await self_destruct() elif mode == 'cmd': command_line() + elif mode in ['l', 'listen']: + listen.run() else: print('\nunknown mode. run with no arguments or -h for help and modes') @@ -59,9 +60,9 @@ def main(): global PROGRAM_ARGS parser = init_argparse() - # if len(sys.argv) < 2: - # parser.print_help() - # return + if len(sys.argv) < 2: + parser.print_help() + return PROGRAM_ARGS = parser.parse_args() @@ -77,6 +78,7 @@ def main(): ttq.TalentTweetQueue() ## Asynchronous execution + print('beginning async main') nest_asyncio.apply() asyncio.run(async_main()) diff --git a/src/scraper.py b/src/scraper.py index 9e77dc9..b1dc654 100644 --- a/src/scraper.py +++ b/src/scraper.py @@ -2,7 +2,6 @@ from os.path import exists from time import sleep from datetime import datetime, timedelta -from dotenv import dotenv_values import pytz from tweety import Twitter @@ -10,18 +9,33 @@ from tweety.types import * from tweety.exceptions_ import * from tweety.filters import SearchFilters +from account_pool import AccountPool from tweety_utils import * from talenttweet import * import talent_lists class Scraper: def __init__(self): - creds = dotenv_values() - self.app = Twitter("session") - # if exists("session.json"): - # self.app.connect() - # else: - self.app.sign_in(creds["scraper_username"], creds["scraper_password"]) + Scraper.instance = self + self.__account = AccountPool() + self.try_login() + + def try_login(self) -> bool: + acc = self.__account.next() + if acc is not None: + name = acc[0] + print(f"using {name}") + self.app = Twitter(name) + if exists(f"{name}.json"): + try: + self.app.connect() + except: + self.app.sign_in(*acc) + else: + self.app.sign_in(*acc) + return True + print('exhausted all accounts!') + return False # since MUST BE TIMEZONE AWARE # usage example: since=datetime(2023, 8, 1).replace(tzinfo=pytz.utc) @@ -43,11 +57,29 @@ class Scraper: # malformed tweet check nonlocal reached_backdate try: - tweet.author - except AttributeError: - print("skipping malformed tweet: {tweet}") + tweet.author.id + except: + print(f"skipping malformed tweet: {tweet}") return + # recover lost info + if tweet.is_retweet: + if tweet.retweeted_tweet is None: + print(f'{tweet.author.username}/{tweet.id} is missing the RT! Recovering...') + tweet.retweeted_tweet = self.app.tweet_detail(str(tweet.id)).retweeted_tweet + if tweet.retweeted_tweet.author is None: + print(f'WARNING: {tweet.author.username}/{tweet.id} is missing the RT author! Recovering details...') + tweet.retweeted_tweet = self.app.tweet_detail(tweet.retweeted_tweet.id) + + if tweet.is_quoted: + if tweet.quoted_tweet is None: # quoted tweet is deleted + # print(f'{tweet.author.username}/{tweet.id} is missing the QRT! Recovering...') + # tweet.quoted_tweet = self.app.tweet_detail(str(tweet.id)).quoted_tweet + tweet.is_quoted = False + elif tweet.quoted_tweet.author is None: + print(f'WARNING: {tweet.author.username}/{tweet.id} is missing the QRT author! Recovering details...') + tweet.quoted_tweet= self.app.tweet_detail(tweet.quoted_tweet.id) + # fix reply if it exists # if tweet.is_reply and tweet.replied_to is None: # tweet.replied_to = self.app.tweet_detail(tweet._original_tweet['in_reply_to_status_id_str']) @@ -72,22 +104,30 @@ class Scraper: elif isinstance(e, TweetThread): # FIXME: rework when replied_to is fixed (currently populates user_mentions) # latest tweet in thread = og author's reply - add_tweet(e[0]) for t in e: add_tweet(t) cur = search.cursor except UnknownError: print("UnknownError occurred, probably rate-limited") - print("sleeping for 1 minute...") - sleep(60) + # traceback.print_exc() + if not self.try_login(): + print("sleeping for 1 minute...") + sleep(60) + print() + self.try_login() tweets.sort(key=lambda t: t.id) return tweets - def get_cross_ttweets_from_user(self, username: str, since: datetime = None) -> list[TalentTweet]: + def get_cross_ttweets_from_user(self, username: str, since_date: str = None) -> list[TalentTweet]: + if since_date is not None: + d = since_date.split('-') + since = datetime(*[int(x) for x in d]).replace(tzinfo=pytz.utc) + else: + since = None tweets = self.get_tweets_from_user(username, since) - print_tweets(tweets) + # print_tweets(tweets) ret: list[TalentTweet] = [] for t in tweets: tt = TalentTweet.create_from_tweety(t) @@ -95,7 +135,8 @@ class Scraper: ret.append(tt) return ret -talent_lists.init() -s = Scraper() -ttweets = s.get_cross_ttweets_from_user("pomurainpuff", since=datetime(2023, 7, 30).replace(tzinfo=pytz.utc)) -print("\n".join([x.__repr__() for x in ttweets])) \ No newline at end of file +if __name__== '__main__': + talent_lists.init() + s = Scraper() + ttweets = s.get_cross_ttweets_from_user("pomurainpuff", since=datetime(2023, 7, 30).replace(tzinfo=pytz.utc)) + print("\n".join([x.__repr__() for x in ttweets])) \ No newline at end of file diff --git a/src/talenttweet.py b/src/talenttweet.py index 969d961..d7a8777 100644 --- a/src/talenttweet.py +++ b/src/talenttweet.py @@ -38,10 +38,12 @@ class TalentTweet: @staticmethod def deserialize(serialized_str: str): - tokens = serialized_str.split('#')[0] - if len(tokens) < 3: + token_check = serialized_str.split('#')[0] + if len(token_check) < 3: raise ValueError('not enough tokens to reconstruct a TalentTweet') + tokens = serialized_str.split() + tweet_id, author_id = int(tokens[0]), int(tokens[1]) date_time = datetime.fromtimestamp(float(tokens[2]), tz=pytz.utc) @@ -59,7 +61,7 @@ class TalentTweet: if tokens[i].isnumeric(): if mode == 'm': # mentions - mentions.add(int(tokens[i])) + mentions.append(int(tokens[i])) continue if mode == 'r': # reply_to reply_to = int(tokens[i]) @@ -69,7 +71,7 @@ class TalentTweet: if mode == 'rt': # retweeted user rt = int(tokens[i]) if mode == 'rtm': # retweet/qrt mentions - rtm = int(tokens[i]) + rtm.append(int(tokens[i])) return TalentTweet( tweet_id=tweet_id, author_id=author_id, @@ -168,15 +170,20 @@ class TalentTweet: def announce_text(self, is_catchup=False): # templates REPLY = '{0} replied to {1}!' - TWEET = '{0} tweeted!' + TWEET = '{0} tweeted mentioning {1}!' RETWEET = '{0} retweeted {1}!' - RETWEET_MENTIONS_B = '{0} shared a tweet mentioning{1}!' + RETWEET_MENTIONS_B = '{0} shared a tweet mentioning {1}!' QUOTE_TWEET = '{0} quote tweeted {1}!' QUOTE_TWEET_MENTIONS_B = '{0} quoted a tweet mentioning {1}!' author_username = f'@/{util.get_username_with_company(self.author_id)}' ret = str() + print_mention_ids = set(self.mentions) + try: print_mention_ids.remove(None) + except: pass + mention_usernames = [f'@/{util.get_username_with_company(x)}' for x in print_mention_ids] + if is_catchup: ret += f'{self.get_datetime_str()}\n' pass @@ -198,16 +205,13 @@ class TalentTweet: else: ret += QUOTE_TWEET.format(author_username, quoted_username) elif len(self.mentions) > 0: # standalone tweet - ret += TWEET.format(author_username) + ret += TWEET.format(author_username, ", ".join(mention_usernames)) + return ret else: raise ValueError(f'TalentTweet {self.tweet_id} has insufficient other parties') - try: print_mention_ids.remove(None) - except: pass - # mention line if len(print_mention_ids) > 0: - mention_usernames = [f'@/{util.get_username_with_company(x)}' for x in print_mention_ids] ret += ( '\nMentioning ' f'{", ".join(mention_usernames)}' diff --git a/src/ttweetqueue.py b/src/ttweetqueue.py index f297491..a602e1f 100644 --- a/src/ttweetqueue.py +++ b/src/ttweetqueue.py @@ -1,6 +1,7 @@ # TODO: move queue structures and file handling here import os import shutil +import traceback import util import talenttweet as tt @@ -55,9 +56,12 @@ class TalentTweetQueue: if len(tokens) == 0 or tokens[0][0] == '#': continue ttweet = tt.TalentTweet.deserialize(line) + # print(f'{ttweet.tweet_id}:\n{ttweet}') self.ttweets_dict[ttweet.tweet_id] = ttweet print(f'Found {len(self.finished_user_dates)} scraped accounts and {len(self.ttweets_dict)} tweets in queue.') - except: pass + except: + traceback.print_exc() + pass # finished ttweets try: with open(self.finished_ttweets_path, 'r') as f: diff --git a/src/twapi.py b/src/twapi.py index c7f9e6f..9ea4126 100644 --- a/src/twapi.py +++ b/src/twapi.py @@ -2,9 +2,9 @@ import datetime import traceback import asyncio +from dotenv import dotenv_values import tweepy -import api_secrets import talenttweet as tt import talent_lists as tl import ttweetqueue as ttq @@ -70,24 +70,25 @@ class TwAPI: def __init__(self): + creds = dotenv_values() TwAPI.instance = self self.client = tweepy.Client( - bearer_token=api_secrets.bearer_token(), - consumer_key=api_secrets.api_key(), consumer_secret=api_secrets.api_secret(), - access_token=api_secrets.access_token(), access_token_secret=api_secrets.access_secret() + consumer_key=creds['app_key'], consumer_secret=creds['app_secret'], + access_token=creds['user_token'], access_token_secret=creds['user_secret'] ) - self.api = tweepy.API( - auth=tweepy.OAuthHandler( - consumer_key=api_secrets.api_key(), consumer_secret=api_secrets.api_secret(), - access_token=api_secrets.access_token(), access_token_secret=api_secrets.access_secret() - ) - ) - try: - self.me = self.client.get_me().data - except Exception as e: - print('Did you setup secrets.ini?') - raise e - print(f'Assuming the account of @{self.me.data["username"]} ({self.me["id"]})') + # self.api = tweepy.API( + # auth=tweepy.OAuthHandler( + # consumer_key=api_secrets.api_key(), consumer_secret=api_secrets.api_secret(), + # access_token=api_secrets.access_token(), access_token_secret=api_secrets.access_secret() + # ) + # ) + + # try: + # self.me = self.client.get_me(wait_on_rate_limit=True).data + # except Exception as e: + # print('Failed to login!') + # raise e + # print(f'Assuming the account of @{self.me.data["username"]} ({self.me["id"]})') ## ---[COMMENT OUT WHEN NOT IN USE]--- # async def nuke_tweets(self): @@ -154,17 +155,16 @@ class TwAPI: # NO DRY-RUN: actually post tweet # main tweet: text + screenshot try: - print('creating main QRT w/ screenshot...', end='') + print('creating main QRT w/ screenshot...') media_ids = [await self.get_ttweet_image_media_id(ttweet)] twt_resp = await self.post_tweet(text, media_ids=media_ids, quote_tweet_id=ttweet.tweet_id) print('done') except: print('error occurred trying to create main tweet, falling back to URL-main + reply screencap format') traceback.print_exc() - text += f"\n{ttweet_url}" try: - print('posting main tweet...', end='') - twt_resp = await self.post_tweet(text) + print('posting main tweet...') + twt_resp = await self.post_tweet(text, quote_tweet_id=ttweet.tweet_id) print('done') twt_id = twt_resp.data['id'] # if ttweet.reply_to is not None: From 79e5fca9cca713d4dd78cf1036499ed946da0693 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Thu, 17 Aug 2023 03:04:41 -0700 Subject: [PATCH 13/18] added wait-for-avail-scraping-account sleep time --- src/scraper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scraper.py b/src/scraper.py index b1dc654..ba2534e 100644 --- a/src/scraper.py +++ b/src/scraper.py @@ -112,8 +112,8 @@ class Scraper: print("UnknownError occurred, probably rate-limited") # traceback.print_exc() if not self.try_login(): - print("sleeping for 1 minute...") - sleep(60) + print("sleeping for 2 minutes...") + sleep(120) print() self.try_login() From fe1749bbe017e5648ab615f3f4c181866c7166d0 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Fri, 18 Aug 2023 01:34:25 -0700 Subject: [PATCH 14/18] clean up code, setup in ready-to-run state --- README.md | 42 ++++++++----- lists/nijien.txt | 6 +- retweet notes.txt => programmer notes.txt | 13 +++- src/account_pool.py | 4 ++ src/catchup.py | 70 +++++++++++---------- src/listen.py | 8 +-- src/main.py | 35 ++++------- src/scraper.py | 32 +++++++--- src/talent_lists.py | 16 +++-- src/talenttweet.py | 75 ++++++++++++++--------- src/ttweetqueue.py | 9 ++- src/twapi.py | 63 ++++--------------- src/util.py | 29 ++------- 13 files changed, 203 insertions(+), 199 deletions(-) rename retweet notes.txt => programmer notes.txt (59%) diff --git a/README.md b/README.md index 394e8d0..c036f0a 100644 --- a/README.md +++ b/README.md @@ -7,25 +7,39 @@ Twitter bot that tracks cross-company interactions between the non-JP branches o ## `.env` These need to be defined in a `.env` file at the project root (outside of `src`): -``` -# Scweet (scraping) -SCWEET_EMAIL= -SCWEET_USERNAME= -SCWEET_PASSWORD= -# Twitter API bot keys (posting) -api_key= -api_secret= -oauth1_access_token= -oauth1_access_secret= -bearer_token= +### Scraper Credentials +To get around rate limitations imposed on users, we scrape with multiple accounts. Each account is defined in the file using the following format: +``` +scraper_usernameX=twitter_username +scraper_passwordX=twitter_password +``` +where `X` is a number starting from 0, increasing by 1 for each account added. For instance: +``` +scraper_username0= +scraper_password0= +scraper_username1= +scraper_password1= +``` +The first account (`scraper_username0` and `scraper_password0`) will be used to attempt scraping private accounts. Make sure this account follows any private accounts that you want to scrape! +### Twitter API Stuff +The following keys/tokens are used for the official API via `tweepy`. We mainly use these to just post tweets. +``` +app_key= +app_secret= +user_token= +user_secret= +``` +### Screenshot Cookie *(optional)* +This is the authentication token obtained from a browser when signed in on the Twitter website. It's only needed if you want to screenshot tweets from privated accounts. Make sure the token belongs to an account that follows desired private accounts! Maybe have it belong to `scraper_username0`? +``` +web_auth_token= ``` ## Running modes The bot may run in these modes: -* Catch-up (`c`): intended to run only once, scan all accounts for cross-company tweets and post them. Terminate when done posting all. - - use `--auto-listen` to switch to listen mode when finished -* Listen (`l`): listens for tweets from list, sharing it if it's cross-company +* Pass no argument to run in listen mode, which scrapes all accounts in the *list* folder at an interval. + * Pass `--straight-to-queue` to process the queue first before attempting to scrape. * Command-line (`cmd`): an interactive mode for manual control and debugging (drops into Python interpretor) *Created for the spirit of entertainment and in the name of unity.* ❤ diff --git a/lists/nijien.txt b/lists/nijien.txt index 14044ba..2fd5fb1 100644 --- a/lists/nijien.txt +++ b/lists/nijien.txt @@ -15,7 +15,7 @@ # --- [Ethyria] --- 1437952405283426310 MillieParfait 1437963160544284675 EnnaAlouette -1437959162651156484 NinaKosaka +1437959162651156484 NinaKosaka p 1437961007029227520 ReimuEndou # --- [Luxiem]--- @@ -28,7 +28,7 @@ # --- [Noctyx] --- 1490867613915828224 alban_knox 1491195742123397124 uki_violeta -1492604168145539072 Yugo_Asuma +1492604168145539072 Yugo_Asuma p 1493392149664219138 Fulgur_Ovid 1493394108014292993 sonny_brisko @@ -44,7 +44,7 @@ 1589536631324692480 MelocoKyoran 1589524401170833409 HexHaywire 1589531775058968576 D_Dropscythe -1589539582399348738 ZaionLanZa +1589539582399348738 ZaionLanZa p 1591995159901663232 KotokaTorahime 1589791076709171201 Ver_Vermillion diff --git a/retweet notes.txt b/programmer notes.txt similarity index 59% rename from retweet notes.txt rename to programmer notes.txt index 04809b4..3cf5b52 100644 --- a/retweet notes.txt +++ b/programmer notes.txt @@ -1,5 +1,8 @@ -possible combinations which involve a "target cross-tweeter" B +[scraper rate limitations] +50 searches/pages every 15 minutes + - max 20 tweets per search +[possible combinations which involve a "target cross-tweeter" B] A retweets B - B's tweet may have cross-mentions (B1, B2, etc.) - rt_author_id=B; rt_mentions=B1,B2,... @@ -12,5 +15,13 @@ A quotes a tweet from B A quotes a tweet mentioning B - quote_retweeted=...; rt_mentions=B... +A replies to B + r = B +A replies to a tweet mentioning B + - r=...; rtm=B1,B2,... + -- NO -- A retweets a tweet that quotes a tweet mentioning B? + +[potential code change] +rtm --> tgm (target tweet's mentions) \ No newline at end of file diff --git a/src/account_pool.py b/src/account_pool.py index 1a655d7..384bbd2 100644 --- a/src/account_pool.py +++ b/src/account_pool.py @@ -18,6 +18,10 @@ class AccountPool: else: break + def use_index(self, idx): + self.__idx = idx + return self.current() + def current(self): if 0 <= self.__idx < len(self.__accounts): return self.__accounts[self.__idx] diff --git a/src/catchup.py b/src/catchup.py index ffa638b..3f42c14 100644 --- a/src/catchup.py +++ b/src/catchup.py @@ -17,13 +17,12 @@ from twapi import TwAPI import talenttweet as tt import ttweetqueue as ttq -PROGRAM_ARGS = None -safe_to_post_tweets = False +safe_to_post_tweets = True errored = False # Returns a list of sorted and filtered TalentTweets (should # be equivalent to queue.txt) -async def get_cross_talent_tweets(): +async def get_cross_tweets_online(): global safe_to_post_tweets scraper = Scraper() @@ -38,9 +37,9 @@ async def get_cross_talent_tweets(): # tweets = get_user_tweets(talent_id, since_date=queue.finished_user_dates.get(talent_id, None)) since_date = queue.finished_user_dates.get(talent_id, None) ttweets = scraper.get_cross_ttweets_from_user(talent_username, since_date=since_date) + print(f'got {len(ttweets)} TalentTweets') for ttweet in ttweets: - if ttweet.tweet_id not in queue.ttweets_dict \ - and ttweet.tweet_id not in queue.finished_ttweets \ + if ttweet.tweet_id not in queue.finished_ttweets \ and ttweet.is_cross_company(): queue.add_ttweet(ttweet) except KeyboardInterrupt as e: @@ -68,9 +67,9 @@ async def get_cross_talent_tweets(): # return False = errored or we posted at least one ttweet # return True = we didn't post a single ttweet async def process_queue() -> bool: - global PROGRAM_ARGS global errored - WAIT_TIME = 60*3 + + WAIT_TIME = 60*15 ttweets_posted = 0 errored = False @@ -81,13 +80,10 @@ async def process_queue() -> bool: print('Posting queue is empty!') return True - if PROGRAM_ARGS.announce_catchup: - TwAPI.instance.post_tweet(text=f'Starting to catch up through {queued_ttweets_count} logged tweets.') - try: while not queue.is_empty(): ttweet = queue.get_next_ttweet() - tweet_was_successful = await TwAPI.instance.post_ttweet(ttweet, is_catchup=True) + tweet_was_successful = await TwAPI.instance.post_ttweet(ttweet) print('running queue.good()...') queue.good() @@ -103,9 +99,6 @@ async def process_queue() -> bool: print('Unhandled error occurred while posting tweets from queue.') errored = True traceback.print_exc() - else: - if PROGRAM_ARGS.announce_catchup: - await TwAPI.instance.post_tweet('Finished with catch-up tweets!') if errored or ttweets_posted > 0: return False @@ -113,26 +106,39 @@ async def process_queue() -> bool: # return True = no problems # return False = issue occurred where we couldn't post all past tweets properly -async def run(): +async def run(PROGRAM_ARGS): global errored global safe_to_post_tweets queue = ttq.TalentTweetQueue.instance - while True: - await get_cross_talent_tweets() - print(f'{queue.get_count()} cross-company tweets to attempt sharing.') - try: - if safe_to_post_tweets: - if await process_queue(): - print('Posted no new tweets; we\'re caught up!') - return True - else: - print('Tweets were not retrieved cleanly.') + + async def queue_loop(): + while True: + print(f'{queue.get_count()} cross-company tweets to attempt sharing.') + try: + if safe_to_post_tweets: + if await process_queue(): + print('Posted no new tweets; we\'re caught up!') + return True + else: + print('Tweets were not retrieved cleanly.') + return False + except KeyboardInterrupt: + print('Interrupting queue processing...') return False - except: - print('Unhandled error occurred while running catch up in posting phase.') - traceback.print_exc() - return False - - if errored: - return False + except: + print('Unhandled error occurred while running catch up in posting phase.') + traceback.print_exc() + return False + + if errored: + return False + + await get_cross_tweets_online() + + if PROGRAM_ARGS.straight_to_queue: + print('Processing queue first before pulling tweets...') + return await queue_loop() + else: + await get_cross_tweets_online() + return await queue_loop() diff --git a/src/listen.py b/src/listen.py index e1ccfd1..131942c 100644 --- a/src/listen.py +++ b/src/listen.py @@ -9,13 +9,13 @@ import catchup errors_encountered = 0 -def run(): +def run(PROGRAM_ARGS): global errors_encountered while True: try: - asyncio.run(catchup.run()) - print('Sleeping for 30 minutes...') - sleep(1800) # run every half-hour + asyncio.run(catchup.run(PROGRAM_ARGS)) + print('Sleeping for 10 minutes...') + sleep(60*10) # run every 10 minutes except KeyboardInterrupt: print('Interrupt signal received. Exiting listen mode.') print(f'{errors_encountered} errors encountered throughout session.') diff --git a/src/main.py b/src/main.py index ed60f60..09f2e56 100644 --- a/src/main.py +++ b/src/main.py @@ -15,44 +15,34 @@ from twapi import TwAPI PROGRAM_ARGS = None MODES_HELP_STR = '''mode to run the bot at: -l,listen: listen for new tweets from all accounts; will not terminate unless error occurs -c,catchup: scan all tweets from all accounts; will terminate when done -d,delete-all: delete all tweets on account provided by secrets.ini; make sure the function is uncommented in twapi.py''' + scrape accounts in lists and post cross-company tweets if relevant +cmd drop into Python interpretor with access to initialized variables''' def init_argparse(): p = argparse.ArgumentParser(description='Twitter bot that follows interactions between Nijisanji EN/ID and hololive EN/ID members.', formatter_class=RawTextHelpFormatter) - p.add_argument('mode', nargs='?', \ - help=MODES_HELP_STR) - p.add_argument('--no-delay', action='store_true', help='In self-destruct mode, clear tweets without safety waiting.') + p.add_argument('mode', nargs='?', help=MODES_HELP_STR) + p.add_argument('--no-listen', action='store_true', help='Run one scraping-posting cycle without waiting to run again.') + p.add_argument('--straight-to-queue', action='store_true', help='Go through queue first before attempting to pull tweets.') return p def command_line(): # TODO (extra): implement command line mode for manually controlling the bot - print('Shell coming soon. For now, here\'s a Python interpretor.') + print('Here\'s a Python interpretor.') code.interact(local=globals()) - pass - -async def self_destruct(): - if not PROGRAM_ARGS.no_delay: - print('\033[31;6m-----DELETING ALL TWEETS IN 10 SECONDS!! PRESS CTRL+C TO CANCEL.-----\033[0m') - await asyncio.sleep(10) - await TwAPI.instance.nuke_tweets() async def async_main(): global PROGRAM_ARGS if PROGRAM_ARGS.mode == None: - await catchup.run() + if PROGRAM_ARGS.no_listen: + await catchup.run(PROGRAM_ARGS) + else: + listen.run(PROGRAM_ARGS) return mode = PROGRAM_ARGS.mode.lower() - if mode in ['d', 'delete-all']: - print('WARNING: SELF-DESTRUCT MODE') - await self_destruct() - elif mode == 'cmd': + if mode == 'cmd': command_line() - elif mode in ['l', 'listen']: - listen.run() else: print('\nunknown mode. run with no arguments or -h for help and modes') @@ -66,8 +56,6 @@ def main(): PROGRAM_ARGS = parser.parse_args() - ## We expect to run in some mode now. - # Initialize shared API instance TwAPI() @@ -78,7 +66,6 @@ def main(): ttq.TalentTweetQueue() ## Asynchronous execution - print('beginning async main') nest_asyncio.apply() asyncio.run(async_main()) diff --git a/src/scraper.py b/src/scraper.py index ba2534e..7a1f8f8 100644 --- a/src/scraper.py +++ b/src/scraper.py @@ -20,8 +20,12 @@ class Scraper: self.__account = AccountPool() self.try_login() - def try_login(self) -> bool: - acc = self.__account.next() + def try_login(self, account_idx: int = None) -> bool: + if account_idx is not None: + acc = self.__account.use_index(account_idx) + else: + acc = self.__account.next() + if acc is not None: name = acc[0] print(f"using {name}") @@ -65,9 +69,10 @@ class Scraper: # recover lost info if tweet.is_retweet: if tweet.retweeted_tweet is None: - print(f'{tweet.author.username}/{tweet.id} is missing the RT! Recovering...') - tweet.retweeted_tweet = self.app.tweet_detail(str(tweet.id)).retweeted_tweet - if tweet.retweeted_tweet.author is None: + print(f'{tweet.author.username}/{tweet.id} is missing the RT! It\'s probably nothing...') + # tweet.retweeted_tweet = self.app.tweet_detail(str(tweet.id)).retweeted_tweet + tweet.is_retweet = False + elif tweet.retweeted_tweet.author is None: print(f'WARNING: {tweet.author.username}/{tweet.id} is missing the RT author! Recovering details...') tweet.retweeted_tweet = self.app.tweet_detail(tweet.retweeted_tweet.id) @@ -78,17 +83,20 @@ class Scraper: tweet.is_quoted = False elif tweet.quoted_tweet.author is None: print(f'WARNING: {tweet.author.username}/{tweet.id} is missing the QRT author! Recovering details...') - tweet.quoted_tweet= self.app.tweet_detail(tweet.quoted_tweet.id) + tweet.quoted_tweet = self.app.tweet_detail(tweet.quoted_tweet.id) # fix reply if it exists # if tweet.is_reply and tweet.replied_to is None: - # tweet.replied_to = self.app.tweet_detail(tweet._original_tweet['in_reply_to_status_id_str']) + # tweet.replied_to = self.app.tweet_detail(tweet.original_tweet['in_reply_to_status_id_str']) tweets.append(tweet) if not reached_backdate and int(tweet.author.id) == uid and tweet.date <= since: print("reached backdate") reached_backdate = True + if uid in talent_lists.privated_accounts: + self.try_login(0) + while not reached_backdate: try: # uts = self.app.get_tweets(uid, replies=True, cursor=cur) @@ -110,8 +118,14 @@ class Scraper: cur = search.cursor except UnknownError: print("UnknownError occurred, probably rate-limited") - # traceback.print_exc() - if not self.try_login(): + if uid in talent_lists.privated_accounts: + print("sticking pvt-accessible account. sleeping for 2 minutes...") + sleep(120) + print() + l = self.try_login(0) + else: + l = self.try_login() + if not l: print("sleeping for 2 minutes...") sleep(120) print() diff --git a/src/talent_lists.py b/src/talent_lists.py index 6f32a37..dbe9f35 100644 --- a/src/talent_lists.py +++ b/src/talent_lists.py @@ -6,6 +6,7 @@ niji_en: dict[int, str] = dict() niji_exid: dict[int, str] = dict() talents: dict[int, str] = dict() talents_company: dict[int, str] = dict() +privated_accounts: dict[int, str] = dict() test_talents = dict() @@ -16,12 +17,15 @@ def __create_dict(file, _dict, company): with open(file, 'r') as f: for line in f: words = line.split() - if len(words) == 2 and line[0] != '#': - id, name = line.split() + if len(words) >= 2 and line[0] != '#': + t = line.split() + id, name = int(t[0]), t[1] # name = f'{util.get_username_online(id, default=name)}' # attempt to get updated name - talents[int(id)] = name - _dict[int(id)] = name - talents_company[int(id)] = company + talents[id] = name + _dict[id] = name + talents_company[id] = company + if len(words) > 2 and words[2] == 'p': + privated_accounts[id] = name def init(): global holo_en global holo_id @@ -36,7 +40,7 @@ def init(): # nijiEN __create_dict(f'{util.get_project_dir()}/lists/nijien.txt', niji_en, 'nijiEN') # nijiexID - __create_dict(f'{util.get_project_dir()}/lists/nijiexid.txt', niji_exid, 'nijiex-ID') + __create_dict(f'{util.get_project_dir()}/lists/nijiexid.txt', niji_exid, 'nijiex\'ID') # TODO: nijiex-KR test_talents = holo_en diff --git a/src/talenttweet.py b/src/talenttweet.py index d7a8777..2f0bb3b 100644 --- a/src/talenttweet.py +++ b/src/talenttweet.py @@ -5,7 +5,8 @@ import platform import pytz from tweety.types import * -from talent_lists import is_cross_company +# from talent_lists import is_cross_company, talents +import talent_lists as tl import util class TalentTweet: @@ -94,7 +95,7 @@ class TalentTweet: date_time=tweety.date, text=tweety.text, mrq=( [int(x.id) for x in tweety.user_mentions], - int(tweety._original_tweet['in_reply_to_user_id_str']) if tweety.is_reply else None, + int(tweety.original_tweet['in_reply_to_user_id_str']) if tweety.is_reply else None, int(tweety.quoted_tweet.author.id) if tweety.quoted_tweet is not None else None ), rt_author_id=tweety.retweeted_tweet.author.id if tweety.is_retweet else None, @@ -108,15 +109,22 @@ class TalentTweet: self.date_time = date_time self.text = text - # filter twitter users to only be cross-company - self.mentions = {x for x in mrq[0] if is_cross_company(author_id, x)} - self.reply_to = mrq[1] if mrq[1] is not None and is_cross_company(author_id, mrq[1]) else None - self.quote_tweeted = mrq[2] + # filter users to only be talents + self.mentions = {x for x in mrq[0] if x in tl.talents} + self.rt_mentions = {x for x in rt_mentions if x in tl.talents} - # rt'd/quoted tweet contains cross-company names? - self.rt_mentions = {x for x in rt_mentions if is_cross_company(author_id, x)} + self.reply_to = mrq[1] + self.quote_tweeted = mrq[2] self.rt_author_id = rt_author_id + try: self.mentions.remove(self.reply_to) + except: pass + + # -1 if user is not in company + self.reply_to = self.reply_to if self.reply_to is None or self.reply_to in tl.talents else -1 + self.quote_tweeted = self.quote_tweeted if self.quote_tweeted is None or self.quote_tweeted in tl.talents else -1 + self.rt_author_id = self.rt_author_id if self.rt_author_id is None or self.rt_author_id in tl.talents else -1 + # all users involved except for the author self.all_parties = {self.reply_to, self.quote_tweeted, rt_author_id} self.all_parties.update(self.mentions, self.rt_mentions) @@ -124,10 +132,6 @@ class TalentTweet: except: pass try: self.all_parties.remove(self.author_id) except: pass - - # clean up mentions - try: self.mentions.remove(self.reply_to) - except: pass def __repr__(self) -> str: @@ -146,11 +150,11 @@ class TalentTweet: ) def url(self): - return f'https://www.twitter.com/{self.username}/status/{self.tweet_id}' + return util.get_tweet_url(self.tweet_id, self.username) def is_cross_company(self): for other_id in self.all_parties: - if is_cross_company(self.author_id, other_id): + if tl.is_cross_company(self.author_id, other_id): return True return False @@ -167,14 +171,15 @@ class TalentTweet: unpad = '#' if platform.system() == 'Windows' else '-' return self.date_time.strftime(f'%b %{unpad}d %Y, %{unpad}I:%M%p (%Z)') - def announce_text(self, is_catchup=False): + def announce_text(self): # templates - REPLY = '{0} replied to {1}!' TWEET = '{0} tweeted mentioning {1}!' + REPLY = '{0} replied to {1}!' + REPLY_TO_MENTION_B = '{0} replied to a tweet{1}mentioning {1}!' ######################### RETWEET = '{0} retweeted {1}!' - RETWEET_MENTIONS_B = '{0} shared a tweet mentioning {1}!' + RETWEET_MENTIONS_B = '{0} shared a tweet{1}mentioning {2}!' ######################### QUOTE_TWEET = '{0} quote tweeted {1}!' - QUOTE_TWEET_MENTIONS_B = '{0} quoted a tweet mentioning {1}!' + QUOTED_TWEET_MENTIONS_B = '{0} quoted a tweet{1}mentioning {2}!' ######################### author_username = f'@/{util.get_username_with_company(self.author_id)}' ret = str() @@ -184,28 +189,37 @@ class TalentTweet: except: pass mention_usernames = [f'@/{util.get_username_with_company(x)}' for x in print_mention_ids] - if is_catchup: - ret += f'{self.get_datetime_str()}\n' - pass - - rt_mention_names = [util.get_username_with_company(x) for x in self.rt_mentions] + def rtm_msg(TEMPLATE: str, rtm_author_username: str): + if self.rt_author_id != -1: # rtm tweet is not from talent; rtm should be everyone + rtm_names = [f'@/{util.get_username_with_company(x)}' for x in self.rt_mentions] + between = f' from {rtm_author_username} ' + ret += TEMPLATE.format(author_username, between, ", ".join(rtm_names)) + else: # rtm tweet is from a talent; rtm should just be cross company + rtm_names = [f'@/{util.get_username_with_company(x)}' for x in self.rt_mentions if tl.is_cross_company(self.author_id, x)] + ret += TEMPLATE.format(author_username, ' ', ", ".join(rtm_names)) + # Tweet types if self.rt_author_id is not None: # retweet + rt_username = f'@/{util.get_username_with_company(self.rt_author_id)}' if self.rt_author_id != -1 else None if len(self.rt_mentions) > 0: - ret += RETWEET_MENTIONS_B.format(author_username, ", ".join(rt_mention_names)) + rtm_msg(RETWEET_MENTIONS_B, rt_username) else: - ret += RETWEET.format(f'{author_username}', f'@/{util.get_username_with_company(self.rt_author_id)}') + ret += RETWEET.format(author_username, rt_username) elif self.reply_to is not None: # reply - reply_username = f'@/{util.get_username_with_company(self.reply_to)}' - ret += REPLY.format(author_username, reply_username) - elif self.quote_tweeted is not None: # qrt - quoted_username = f'@/{util.get_username_with_company(self.quote_tweeted)}' + reply_username = f'@/{util.get_username_with_company(self.reply_to)}' if self.reply_to != -1 else None if len(self.rt_mentions) > 0: - ret += QUOTE_TWEET_MENTIONS_B.format(author_username, ", ".join(rt_mention_names)) + rtm_msg(REPLY_TO_MENTION_B, reply_username) + else: + ret += REPLY.format(author_username, reply_username) + elif self.quote_tweeted is not None: # qrt + quoted_username = f'@/{util.get_username_with_company(self.quote_tweeted)}' if self.quote_tweeted != -1 else None + if len(self.rt_mentions) > 0: + rtm_msg(QUOTED_TWEET_MENTIONS_B, quoted_username) else: ret += QUOTE_TWEET.format(author_username, quoted_username) elif len(self.mentions) > 0: # standalone tweet ret += TWEET.format(author_username, ", ".join(mention_usernames)) + f'[{self.get_datetime_str()}]\n' return ret else: raise ValueError(f'TalentTweet {self.tweet_id} has insufficient other parties') @@ -217,4 +231,5 @@ class TalentTweet: f'{", ".join(mention_usernames)}' ) + ret += f'\n\n{self.get_datetime_str()}' return ret diff --git a/src/ttweetqueue.py b/src/ttweetqueue.py index a602e1f..7e51391 100644 --- a/src/ttweetqueue.py +++ b/src/ttweetqueue.py @@ -74,8 +74,8 @@ class TalentTweetQueue: return self.get_count() <= 0 def add_ttweet(self, ttweet): - self.__sorted = False self.ttweets_dict[ttweet.tweet_id] = ttweet + self.__sorted = False def get_ttweet(self, id): return self.ttweets_dict[id] @@ -84,7 +84,10 @@ class TalentTweetQueue: self.is_good = False if os.path.exists(self.current_ttweet_path): with open(self.current_ttweet_path, 'r') as f: - return tt.TalentTweet.deserialize(f.readline()) + ttweet = tt.TalentTweet.deserialize(f.readline()) + if ttweet.tweet_id in self.ttweets_dict: + self.ttweets_dict.pop(ttweet.tweet_id) + return ttweet self.__sort_ttweets_dict() key = list(self.ttweets_dict.keys())[0] @@ -109,6 +112,7 @@ class TalentTweetQueue: # overwrite queue.txt def save_file(self): + print('saving file...', end='') shutil.copyfile(self.queue_path, self.queue_backup_path) self.__sort_ttweets_dict() with open(self.queue_path, 'w') as f: @@ -121,6 +125,7 @@ class TalentTweetQueue: # write sorted ttweets for ttweet in self.ttweets_dict.values(): f.write(ttweet.serialize() + '\n') + print('done') def add_finished_tweet(self, id): self.finished_ttweets.append(id) diff --git a/src/twapi.py b/src/twapi.py index 9ea4126..712a787 100644 --- a/src/twapi.py +++ b/src/twapi.py @@ -76,54 +76,18 @@ class TwAPI: consumer_key=creds['app_key'], consumer_secret=creds['app_secret'], access_token=creds['user_token'], access_token_secret=creds['user_secret'] ) - # self.api = tweepy.API( - # auth=tweepy.OAuthHandler( - # consumer_key=api_secrets.api_key(), consumer_secret=api_secrets.api_secret(), - # access_token=api_secrets.access_token(), access_token_secret=api_secrets.access_secret() - # ) - # ) + self.api = tweepy.API( + auth=tweepy.OAuthHandler( + consumer_key=creds['app_key'], consumer_secret=creds['app_secret'], + access_token=creds['user_token'], access_token_secret=creds['user_secret'] + ) + ) - # try: - # self.me = self.client.get_me(wait_on_rate_limit=True).data - # except Exception as e: - # print('Failed to login!') - # raise e - # print(f'Assuming the account of @{self.me.data["username"]} ({self.me["id"]})') - - ## ---[COMMENT OUT WHEN NOT IN USE]--- - # async def nuke_tweets(self): - # async def delete_tweet(id): - # try: - # self.client.delete_tweet(id) - # except tweepy.TooManyRequests as e: - # wait_for = float(e.response.headers["x-rate-limit-reset"]) - datetime.datetime.now().timestamp() + 1 - # print(f'\thit rate limit deleting {id}, retrying in {wait_for} seconds...') - # await asyncio.sleep(wait_for) - # print('continuing...') - # await delete_tweet(id) - - # print(f'Retrieving all of {self.me["username"]}\'s tweets...') - # tweets = self.get_all_tweet_ids_from_user(self.me['id']) - - # print(f'Retrieved {len(tweets)} tweets.') - # if not len(tweets) > 0: - # print('No tweets obtained. Make sure the profile is public.') - # return - - # print(f'Deleting {len(tweets)} tweets...') - # deleted_count = 0 - # try: - # for tweet in tweets: - # print(f'deleted {deleted_count}/{len(tweets)}') - # await delete_tweet(tweet.id) - # await asyncio.sleep(0.5) - # deleted_count += 1 - # except: - # print('Unhandled error occurred while trying to delete tweets.') - # traceback.print_exc() - # print('Try running again.') - # else: - # print('Saul Gone') + try: + self.me = self.client.get_me().data + print(f'Assuming the account of @{self.me.data["username"]} ({self.me["id"]})') + except: + pass async def post_tweet(self, text='', media_ids: list=None, reply_to_tweet: int=None, quote_tweet_id: int=None): try: @@ -142,7 +106,7 @@ class TwAPI: # return True = successfully posted a single ttweet # return False = did not post ttweet (duplicate) - async def post_ttweet(self, ttweet: tt.TalentTweet, is_catchup=False, dry_run=False): + async def post_ttweet(self, ttweet: tt.TalentTweet, dry_run=False): print(f'------{ttweet.tweet_id} ({util.get_username_local(ttweet.author_id)})------') text = ttweet.announce_text() @@ -167,9 +131,6 @@ class TwAPI: twt_resp = await self.post_tweet(text, quote_tweet_id=ttweet.tweet_id) print('done') twt_id = twt_resp.data['id'] - # if ttweet.reply_to is not None: - # re_ttweet = tt.TalentTweet(tweet_id=ttweet.reply_to, author_id=) - # media_ids.insert(0, await self.get_ttweet_image_media_id()) try: print('creating reply img...', end='') diff --git a/src/util.py b/src/util.py index 289c273..259d8d4 100644 --- a/src/util.py +++ b/src/util.py @@ -4,11 +4,12 @@ import os import sys import traceback from datetime import datetime +from dotenv import dotenv_values import tweepy import pytz import twint -#import twapi +import twapi from tweetcapture import TweetCapture from recrop import fix_aspect_ratio @@ -53,11 +54,12 @@ def get_key_from_value(d: dict, val): async def create_ttweet_image(ttweet): tc = TweetCapture() + tc.cookies = [{'name': 'auth_token', 'value': dotenv_values()['web_auth_token']}] if 'linux' in sys.platform: # Linux chromedriver path tc.driver_path = '/usr/bin/chromedriver' filename = f'{get_project_dir()}/img.png' - url = ttweet_to_url(ttweet) + url = ttweet.url() img = None print(url) try: os.remove(filename) @@ -66,7 +68,7 @@ async def create_ttweet_image(ttweet): img = await tc.screenshot( url=url, path=filename, - mode=4, + mode=0, night_mode=1, show_parent_tweets=True ) @@ -80,26 +82,7 @@ async def create_ttweet_image(ttweet): return img def get_tweet_url(id, username): - return f'https://twitter.com/{username}/status/{id}' - -def ttweet_to_url(ttweet): - username = get_username(ttweet.author_id) - return get_tweet_url(ttweet.tweet_id, username) - -# twint -# May not work with short user IDs (ie. 1354241437) -# def get_username_online(id, default=None): -# c = twint.Config() -# c.User_id = id -# c.Store_object = True -# c.Hide_output = True -# try: -# twint.output.users_list.clear() -# twint.run.Lookup(c) -# user = twint.output.users_list[0] -# return user.username -# except: -# return str(default) if default is not None else f'{id}' + return f'https://www.twitter.com/{username}/status/{id}' ## Attempt to pull username from local; pull from online if doesn't exist. def get_username(id): From ac68d91950f06fdf1eaee5066704b23453442588 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Fri, 18 Aug 2023 01:36:38 -0700 Subject: [PATCH 15/18] fix comment --- src/talenttweet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/talenttweet.py b/src/talenttweet.py index 2f0bb3b..8cf228e 100644 --- a/src/talenttweet.py +++ b/src/talenttweet.py @@ -190,11 +190,11 @@ class TalentTweet: mention_usernames = [f'@/{util.get_username_with_company(x)}' for x in print_mention_ids] def rtm_msg(TEMPLATE: str, rtm_author_username: str): - if self.rt_author_id != -1: # rtm tweet is not from talent; rtm should be everyone + if self.rt_author_id != -1: # rtm tweet is from talent; rtm should be everyone rtm_names = [f'@/{util.get_username_with_company(x)}' for x in self.rt_mentions] between = f' from {rtm_author_username} ' ret += TEMPLATE.format(author_username, between, ", ".join(rtm_names)) - else: # rtm tweet is from a talent; rtm should just be cross company + else: # rtm tweet is not from a talent; rtm should just be cross company rtm_names = [f'@/{util.get_username_with_company(x)}' for x in self.rt_mentions if tl.is_cross_company(self.author_id, x)] ret += TEMPLATE.format(author_username, ' ', ", ".join(rtm_names)) From 823cd5e74e6cd141b09500419949647d1124d476 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Fri, 18 Aug 2023 01:56:36 -0700 Subject: [PATCH 16/18] remove queue files to clean up repo root --- finished_ttweets.txt | 998 ---------------------------- queue.txt | 88 --- queue_from_forever_ago.txt | 1264 ------------------------------------ 3 files changed, 2350 deletions(-) delete mode 100644 finished_ttweets.txt delete mode 100644 queue.txt delete mode 100644 queue_from_forever_ago.txt diff --git a/finished_ttweets.txt b/finished_ttweets.txt deleted file mode 100644 index 0a64ea5..0000000 --- a/finished_ttweets.txt +++ /dev/null @@ -1,998 +0,0 @@ -1576430556933672960 -1576489483457134594 -1576509602732322819 -1576660900668317696 -1576716382024015872 -1576720425505415170 -1576755412824600576 -1576995410992041986 -1577051131339235328 -1577055035577417728 -1577068103791366145 -1577070764175466499 -1577083110805876738 -1577098034147573760 -1577193525720272897 -1577193763252076544 -1577100518446403584 -1577592730104254464 -1577681106971000832 -1577751763859521570 -1577814530729525248 -1577857217352863744 -1577858408144936960 -1577866370179805184 -1577940553081176065 -1577996126955122688 -1577998007018328064 -1577998613615345664 -1577998915366162433 -1578245815130210304 -1578516453434675200 -1578638137701838848 -1578973277539926017 -1577940553081176065 -1578245815130210304 -1578516453434675200 -1578638137701838848 -1578973277539926017 -1579547332571705345 -1579547617717293056 -1579548229431365634 -1579554638369796102 -1579608397468794880 -1579628879270281216 -1579629421064921088 -1579758548355276800 -1579758871589302272 -1579768241400197120 -1579789920960655360 -1579790235730575360 -1579900565424701441 -1579964993243873287 -1580181779868430336 -1580258975047888896 -1580259413297807363 -1580259612670177280 -1580260690962501632 -1580261950604222464 -1580262961058263040 -1580265275265495040 -1580268043279958016 -1580269274848849920 -1580269426082910215 -1580269963679477761 -1580276935921655808 -1580277066930434049 -1580279094205259776 -1580283694731776000 -1580381402767720448 -1580449719511572480 -1580449785135640576 -1580476694519246850 -1580480705733148672 -1580516570518159366 -1580517097708609538 -1580576986145038336 -1580664540152606721 -1580838449292406784 -1580838874200227841 -1580842852476915714 -1580842860622286848 -1580879439193595904 -1581315661242171392 -1581359254363148288 -1581359894082424834 -1581512593918685184 -1581628130418384896 -1581628264434782209 -1581639528053321730 -1581644625604845568 -1581655283448745985 -1581732410584371200 -1581945888465096704 -1581945996095160320 -1582035073573888000 -1582082039033262082 -1582135400147001344 -1582135466068828161 -1582142861604446209 -1582148227943436288 -1582167082997014528 -1582188441793548290 -1582191818430521344 -1582191849540026368 -1582280269469716481 -1582283272024834050 -1582371009587740672 -1582461770198761472 -1582555511949385728 -1582617083179044864 -1582617435416694784 -1582635977373544449 -1582636277605617664 -1582773035178024961 -1583173642166292480 -1583176372696252417 -1583204758495457280 -1583526503492440064 -1583563981108629504 -1583575990411747328 -1583576858133921793 -1583668922167234561 -1583669374397059072 -1583689284594245633 -1583863041493413888 -1583888933494358017 -1583908414102519812 -1583909513832574976 -1583968857554767872 -1584008637134970883 -1584441576205320195 -1584442364591939584 -1584480659199754242 -1584528264915083265 -1584528264965787649 -1584622108025712641 -1584628579677126657 -1584629534527488001 -1584629633735364608 -1584649537997533185 -1584728511952011266 -1584753182156414976 -1585073813905375233 -1585097154032435200 -1585098477557555200 -1585112474097176576 -1585144962085048321 -1585145288767201288 -1585145360313618432 -1585145559887020032 -1585145633530597376 -1585161614461849600 -1585199425206394880 -1585208233010290690 -1585277536187064322 -1585424318103945216 -1585449222635225088 -1585511782885380097 -1585553749199978497 -1585828601727156224 -1585841772332302339 -1585871064021671936 -1585872552513134592 -1585872662915559424 -1585875080315559936 -1586100052623171586 -1586179378089582592 -1586374470939254785 -1586376473119686656 -1586392482321158144 -1586643720878706689 -1586737628338077701 -1586840114868629504 -1587216332940247040 -1587235419241996288 -1587255460943511553 -1587280116069142528 -1587290714487943168 -1587336915048894466 -1587459847636779008 -1587670143840112640 -1587670772323155969 -1587828606583115776 -1587830007753105408 -1587830065223684098 -1587891436371861504 -1587931182284517381 -1587936783299321856 -1587947549658386433 -1587983746543894534 -1588044083666231298 -1588093544685719552 -1588095705070981120 -1588100836575440896 -1588101073910104066 -1588101210329927680 -1588128449490612225 -1588149163811913731 -1588293190322253827 -1588366787065675777 -1588678173138780160 -1588762792521498629 -1588796489224294400 -1588958260258025472 -1589150733659963393 -1589439728973217793 -1589529660429602816 -1589532085144150017 -1589532195844411393 -1589540519583289344 -1589570601567809538 -1589641589135413248 -1589669189765451776 -1589916075705085952 -1589916571518242816 -1589918062358450176 -1589977133857722368 -1590885589603479552 -1591306004666675201 -1591307219169148930 -1591336097216622594 -1591336705730437120 -1591730177310359553 -1591839086364741632 -1592029314119520256 -1592086362508840960 -1592189421897797633 -1592514737165971456 -1592635108510474240 -1592743719639592960 -1593330237609164800 -1593612237217964038 -1594025064478801920 -1594025676578824193 -1594025967428608000 -1594305241377284102 -1594336607661760512 -1594475762614423552 -1594499300364779523 -1594537154453147649 -1594543914186317825 -1594581129041915904 -1594595823748550657 -1594617557696348160 -1594648307338739712 -1594648462431498242 -1594650682220769280 -1594705822281789443 -1580268846790778881 -1581083174305599488 -1581083425401806848 -1581088983148560384 -1581096278469246978 -1581513143087681536 -1581514105894350849 -1583568304576565254 -1584600364783144960 -1584612840824049684 -1587558629489926145 -1587669760078188545 -1591359889011773440 -1592042710722314244 -1592082624558034946 -1594945258965848066 -1595094770539712512 -1595144029448339457 -1595159086265339904 -1595190586537283585 -1595209971486306304 -1595520675607113728 -1595531274474643469 -1595605679938707457 -1595677291300163584 -1596235440700407811 -1596630228289605633 -1596636538116382722 -1596733577076449280 -1596797256665292802 -1596833246561521664 -1596996552887013377 -1597029385625665536 -1597051834689941504 -1597053360393228288 -1597265217515581440 -1597265603529969664 -1597266128665214976 -1597270533850427392 -1597321808331825153 -1597325594341310464 -1597426336636669952 -1597636961694797829 -1597654482758045696 -1597698554688335872 -1597699060462673920 -1597845762322223105 -1598567014347542529 -1598586430795747328 -1598796336270217217 -1599022459029966848 -1599310792008269824 -1599324276540297216 -1599350192134033409 -1599694863800279041 -1599703777665503232 -1599996347658051584 -1600462906335690753 -1600496863278407691 -1600499056165404672 -1600628466977832961 -1600669875503972353 -1600671934315786240 -1600677118874353665 -1600700567948767233 -1600705712682053632 -1600706014537564160 -1600706374857822209 -1600709284786053120 -1600709816791334913 -1600752783845560321 -1600772713135554560 -1601053614755446785 -1595709206627246081 -1596251536841400320 -1596364591269023744 -1596365300026421248 -1596500447308939265 -1597857498903764995 -1598352981967376385 -1598477628943794176 -1598552959738343425 -1599118485363490817 -1599279465838018560 -1599291560507895810 -1599297564633083904 -1599337445199253504 -1599358415092191234 -1599423114018185216 -1600363418166648835 -1600369195241205760 -1601558689529593857 -1601595882763616257 -1601642608698482688 -1601749751745372162 -1601754145861484544 -1601768148621217792 -1601877383522713601 -1601878641620967424 -1601878682137931776 -1601879062560968705 -1601881127958253570 -1602009414990430208 -1602012523708010496 -1602205274168852480 -1602205385993101313 -1602275459697639424 -1602506093829099520 -1602508255062020096 -1602540444365508608 -1602658754297843712 -1602743376201359360 -1602753038909050880 -1602766954514767872 -1602768627534467073 -1602800460288647168 -1602804823887421444 -1602832095952658434 -1602847291362123777 -1602851330309357568 -1602853324667191297 -1602963752101580800 -1603073603364233217 -1603074898758631424 -1603287279975686144 -1603297578514546688 -1603822421190008833 -1603838388347584518 -1603840758783815680 -1602766894465175552 -1603867200204701696 -1604185149448634369 -1604237041977495553 -1604261358039711744 -1604403162504232961 -1604513581780844551 -1604516203908632576 -1604519045495234561 -1604522344550043648 -1604556203903709185 -1604726291093233664 -1604726760142262272 -1605034554594832385 -1605065307986735104 -1605083978847096832 -1605252604874874880 -1605264950347399168 -1605266135339134976 -1605266238443241474 -1605268175796445200 -1605268292112945153 -1605341177791684608 -1605354994127241217 -1605398103036948484 -1605415343358447616 -1605552893553016832 -1605563286728196098 -1605707013589151745 -1605763163391156226 -1605763733040599040 -1605978761127636992 -1606359300565237763 -1606359416999116813 -1606359600680271877 -1606359770969014284 -1606385110697316371 -1607544867357433859 -1607925564412932096 -1608271072562139136 -1608325890097782784 -1608536973727367170 -1608539465340915715 -1608780569676251140 -1608826609766891522 -1608827143257223169 -1608827350376140802 -1608827612788576256 -1608829041943138305 -1608888487436255232 -1609051353543761921 -1609272099276984320 -1609724860590329856 -1610129643067310081 -1610130224003559424 -1610710681975914496 -1610710768030486530 -1605758391506599936 -1606347441795731471 -1608806613812678660 -1610391363820081152 -1610883754435973120 -1611004280282157057 -1611004684344475650 -1611005195936489478 -1611144693231779843 -1611145010531160064 -1611145304161624064 -1611160944197132288 -1611175600126935041 -1611231349939240960 -1611594073231720449 -1611669467972644864 -1611954084751474688 -1611957866096889859 -1611976715802214401 -1611978377568874498 -1611978836383522816 -1611989935439118336 -1612041276584792070 -1612222955332984832 -1612223088833200132 -1612223456724279297 -1612486617561985025 -1612492903158267906 -1612559384906813445 -1612828971590193152 -1613178801374232578 -1613178992894398470 -1613179047114346496 -1613179141196767233 -1613179219256958983 -1613179256917352448 -1613179328854122502 -1613411736790437889 -1613760768641572869 -1613784820076929025 -1613792568772366336 -1613799407069323266 -1614026676589019138 -1614045021606666244 -1614052812585066496 -1612040906529456130 -1613408647937417216 -1613822548906774529 -1614451292163944448 -1614504913299714050 -1614514545644666881 -1614516595333959680 -1614620215467454504 -1614654062267617280 -1614666713181880325 -1614668312809738240 -1614677261537673216 -1614677382820134914 -1614706430682054657 -1614712147917041665 -1614713259600535552 -1614713633707274241 -1614713877807398912 -1614714298080854017 -1614714610170613761 -1614714833966104577 -1614715700198928386 -1614727456782712832 -1614744314927779841 -1614801067224748033 -1614801210724474880 -1614801331427966977 -1614831641935122434 -1614882693657034754 -1614892923488960512 -1614896348918321153 -1614897026814324736 -1614910639213871104 -1614928185006919680 -1615072566439288832 -1615160760321343488 -1615198160649060355 -1615744899655307266 -1616432895001772040 -1616436672723304453 -1616496843705442304 -1616501597244387331 -1616506713582235648 -1616507859944222721 -1616509728888324098 -1616537220919492608 -1616539191462223874 -1616564014129070080 -1616565464246718466 -1616568259515502592 -1616582644992548864 -1616591801372258309 -1616592777718165504 -1616593204870287360 -1616999858153422851 -1617004670865805312 -1617136121200377856 -1617158328022618117 -1617233807194705920 -1617272807666372608 -1617317197373911040 -1617317424969437187 -1617318052000940035 -1617318239603662848 -1617318880489398274 -1617318933903671297 -1617499924962369537 -1617541083508142080 -1617719234787962880 -1617732292319006722 -1617734154870980609 -1617737559546941441 -1617737633568010241 -1617743342670073856 -1617746760046346241 -1617757228794281985 -1617764829892349952 -1617862776046899202 -1617870239760535553 -1617886523063664641 -1617930583505801224 -1618423435964846081 -1618424133347594240 -1618542182117539842 -1618542328389697539 -1618548432167403520 -1618554017730396163 -1618567686174429184 -1618677052806434816 -1618830423236448256 -1618834876064747520 -1618844600894107648 -1618845420557590530 -1618845486580129792 -1618983950336282624 -1618988673227436033 -1618991676781137921 -1619066157964881921 -1619099894391738368 -1619180071893479424 -1619202780224446467 -1619305720683585538 -1619399152651358210 -1619654784725950464 -1619687684951384065 -1619774044869632001 -1619775039435587588 -1619778797368389633 -1619832253449912321 -1619856428969398272 -1619924833621671937 -1620003405958873092 -1620227328566792192 -1620346225886396421 -1620346703831502848 -1620664802241835008 -1620674202293653504 -1620684007473287169 -1620711599593119744 -1620849272018333696 -1620952817803612160 -1620953485851394049 -1620954399475658753 -1620955271974780929 -1620957482465230848 -1620958412438929409 -1621310782666416128 -1621311858329407488 -1621312731034030080 -1621314869487091712 -1621403652991721472 -1621425409157140484 -1621561581690826752 -1621574909116948480 -1621591321067622402 -1621741476412821504 -1621743668045676546 -1621744901338841088 -1621746092449878016 -1621750280693092352 -1621750707388055552 -1621750740724350977 -1621751603970842624 -1621755848568733696 -1621771326494244865 -1621797995678429184 -1621798650778206208 -1621799269496913920 -1622048615672733697 -1622519854560595969 -1622565675922501632 -1622893611955240961 -1622953785713913857 -1623465800836284416 -1623471458696728581 -1623472449961529346 -1623707075300986880 -1623709743264190464 -1624643824692908032 -1624646092510879745 -1624648616446152706 -1624648791034044416 -1624649128771739648 -1624659722954747906 -1624853328155779078 -1624860275831672834 -1625010090326761472 -1625058230807277568 -1625118424694247424 -1625734543016660992 -1624520599656624129 -1624521241901076485 -1627514900703584256 -1627522526527062016 -1627529002029297664 -1627529397296320513 -1627563980154671105 -1627566950267371520 -1627573853823307776 -1627574225199591425 -1627606044070277120 -1627606106720686080 -1627608118292287488 -1627635743697866752 -1627639742312468482 -1627654096378384384 -1627655361410011136 -1627655522840358912 -1627861956634746884 -1627864965053808640 -1627934959846707200 -1627953946789838848 -1627985560920006657 -1627985896279797761 -1628003892641566720 -1628004446310649856 -1628007913032753157 -1628008071048925184 -1628047652691775488 -1628269360111579137 -1628283406114230272 -1628283727582498816 -1628333043961430016 -1628335653841944578 -1628640208425197569 -1628652565088059392 -1628680578613780480 -1628680986346266624 -1628717287233421316 -1628786187233099780 -1628786323128741888 -1628786724317941762 -1628786919470407684 -1628795062472835073 -1628795184044728320 -1629272877060046850 -1629272892595634178 -1629272914330636289 -1629335290530676736 -1629341302524346368 -1629402458689794050 -1629454168845893633 -1629468461100589056 -1629758716751798272 -1630026264705896448 -1630026923303911425 -1630027564344569856 -1630032477103222784 -1630033474861056000 -1630035097461346304 -1631048890504867843 -1631542496814825473 -1631590820678860800 -1631676512029282307 -1632102469072297986 -1632283032207114251 -1632285887995449345 -1632414814692601856 -1632556021049856005 -1632560765424259072 -1632629409751416835 -1632641410230026240 -1632641678623522816 -1633423730654478337 -1633657001749397505 -1633659447934611457 -1633664285078421505 -1633664320792940544 -1633911083202117632 -1634528563344064512 -1634531188034654209 -1634539654916751363 -1634620065864728581 -1634683766235856896 -1634746599263043584 -1635151666445193217 -1635154154497916928 -1635154248769093640 -1635168017276633088 -1635253449913925632 -1635253930849599489 -1635259184194408453 -1635260955562237952 -1635263134352162817 -1635336166865248257 -1635366683052482560 -1635462585616314368 -1635474518184722432 -1635477005226618887 -1635511568556507136 -1635566086815875073 -1635745147181735948 -1635797049512951808 -1635798410837245953 -1635817346664153095 -1635891740640174081 -1635904748774887426 -1635973373384871936 -1635976579624095745 -1636360081670545411 -1636360302207078402 -1636370880241754112 -1636377928140468227 -1636418785006567427 -1636576201102721027 -1636599574197403648 -1636635893149876226 -1636729072788045824 -1636766609984352256 -1636767125640728577 -1636984749553254400 -1637021438090829825 -1637022826472574976 -1637022930097049600 -1637035673621528576 -1637058107233939456 -1637061108992442368 -1637061432104849408 -1637205007891066882 -1637210521941442561 -1637872529342869504 -1637924701573099520 -1637925400537108480 -1637925419323371522 -1638013762165788672 -1638017789326663681 -1638044945520132096 -1638093064064364544 -1638094749335707648 -1638174096126263303 -1638240669256024064 -1638250006443352064 -1638276318944022547 -1638288078056857606 -1638496316635357185 -1638515168941592581 -1638525170330599424 -1638564705341001730 -1638565298990383104 -1638565311057391616 -1638565357861609474 -1638565800150958081 -1638565817280503808 -1638694373612298240 -1638694824638398465 -1638977522862813204 -1639137601226522626 -1639150016693452801 -1639159907168718850 -1639159970733359104 -1639160062311796737 -1639161115199541250 -1639170191790596096 -1639179598956208129 -1639257674704453634 -1639347991990636544 -1639472078574481408 -1639703873286701056 -1639705899932557312 -1639766858822762503 -1639796727753457667 -1639796887795511296 -1639829562258325504 -1640367083598024706 -1640438349252141057 -1640728560267608067 -1640730592303108096 -1640733153290592262 -1640733678484574215 -1640733839935909892 -1640924027014332417 -1640924197797998592 -1641206319397969921 -1641206458531323907 -1641207736296677376 -1641213550248050689 -1641214619329916929 -1641214980564344833 -1641331011186466816 -1641527643592949760 -1641638401534476293 -1641682622626627584 -1641775144657117185 -1641842256020635657 -1641842492587819009 -1641843861730230273 -1641843911789367296 -1641848890776223750 -1641863954535178261 -1641874376877563905 -1641881631232499712 -1641905300813803520 -1641920742576070658 -1641931894211133442 -1641931924611448835 -1641968426985099264 -1642044327445639168 -1642052711259701248 -1642271925807329280 -1642363150442934274 -1642366098359328770 -1642386091146899457 -1642386251453202432 -1642386862714294276 -1642387403498459139 -1642389173641568257 -1642390508629807104 -1642391144947671043 -1642391409058787328 -1642392966391287808 -1642401063419662336 -1642404506716590081 -1642407503333044224 -1642442692985593857 -1642683998026563587 -1642874077491892226 -1642936452525834251 -1642965863111479296 -1642974611460505604 -1643151747874172930 -1643375613665787907 -1643383401980870658 -1643456057425948672 -1643456150652739584 -1643514992803696644 -1644101531111952384 -1644101652398612481 -1644228307607486464 -1644804036896641024 -1644808862627770368 -1644825182161256448 -1644835718345228288 -1644835937266827272 -1644836105416474624 -1644905879475752960 -1644947050709102592 -1645279524341563392 -1645279596139671552 -1645279816734883841 -1645693219554852864 -1645875484411015169 -1645877585795256321 -1646006703572283399 -1646010597840936960 -1646010746344538112 -1646011153129021441 -1646122631043883009 -1646131757320933377 -1646133175196991488 -1646135538792153089 -1646135600570040320 -1646136821154467843 -1646192373062569990 -1646560089539743744 -1646566407684132865 -1646747817753362433 -1646748169055924224 -1646751823666073600 -1646753010209681409 -1646928376093892609 -1646928741275439104 -1646932746030161921 -1647078104823922690 -1647103804893167616 -1647225532344352770 -1647227289950904320 -1647276137628696578 -1647438699347468288 -1647526954575486976 -1647527000519884800 -1647633398964408323 -1647707442921234434 -1648048413408399362 -1648049825445974017 -1648052454364413952 -1648053219787161601 -1648149958296588296 -1648237005309095937 -1648241034030370816 -1648259276589404161 -1648380069210107904 -1648411348370444288 -1648424997789265920 -1648520806639759361 -1648623694338486273 -1648701720887713793 -1648703217771880449 -1648726724874752002 -1648733920442322944 -1648768838614278187 -1649308567747407875 -1649308879073816576 -1649355368361762816 -1649476854321913860 -1649477152012640283 -1649649276077703168 -1649774499267747841 -1649777168883363841 -1649801910659022849 -1649804472330387457 -1649804636268773378 -1649805303737663488 -1649821444442845185 -1649870014605037568 -1649947340558442496 -1650001834134720514 -1650055872289329153 -1650059824422596609 -1650060926119460865 -1650111617710063621 -1650111986506801153 -1650260086818832386 -1650262244846968835 -1650561499893493763 -1650561606991097856 -1650561615300034561 -1650561616940003328 -1650573608186441729 -1650574520711798805 -1650574719639322631 -1650575471719899141 -1650575762586492956 -1650625322914664448 -1650626698029703168 -1650642367748411392 -1650682902362079232 -1650684291280699392 -1650685691016384512 -1650690513169948672 -1650812641500098560 -1650874430711648259 -1650950207087996932 -1651032960978567168 -1651034395397029890 -1651034863351283713 -1651035920840245249 -1651323872484945922 -1651327435076513795 -1651355062633918465 diff --git a/queue.txt b/queue.txt deleted file mode 100644 index 0183583..0000000 --- a/queue.txt +++ /dev/null @@ -1,88 +0,0 @@ -# 1283657064410017793 2023-03-20 -# 1283656034305769472 2023-03-20 -# 1283653858510598144 2023-03-20 -# 1283650008835743744 2023-03-20 -# 1283646922406760448 2023-03-20 -# 1363705980261855232 2023-03-20 -# 1409783149211443200 2023-03-20 -# 1409817096523968513 2023-03-20 -# 1409784760805650436 2023-03-20 -# 1409819816194576394 2023-03-20 -# 1409817941705515015 2023-03-20 -# 1536579341332516864 2023-03-20 -# 1536577295632441344 2023-03-20 -# 1536576325296996352 2023-03-20 -# 1536575088996524032 2023-03-20 -# 1397148959798226945 2023-03-20 -# 1540204458042621952 2023-03-20 -# 1234752200145899520 2023-03-20 -# 1234753886520393729 2023-03-20 -# 1235180878449397764 2023-03-20 -# 1328275136575799297 2023-03-20 -# 1328277233492844544 2023-03-20 -# 1328277750000492545 2023-03-20 -# 1486629076005634049 2023-03-20 -# 1486633489101307907 2023-03-20 -# 1486636197908602880 2023-03-20 -# 1390637197167038464 2023-03-20 -# 1390620618001838086 2023-03-20 -# 1390209302120394754 2023-03-20 -# 1413339084076978179 2023-03-20 -# 1413318241804439552 2023-03-20 -# 1413326894435602434 2023-03-20 -# 1437952405283426310 2023-03-20 -# 1437963160544284675 2023-03-20 -# 1437959162651156484 2023-03-20 -# 1437961007029227520 2023-03-20 -# 1465851881180348425 2023-03-20 -# 1465850835951357955 2023-03-20 -# 1465851188562345985 2023-03-20 -# 1465851243167895554 2023-03-20 -# 1465858739970273281 2023-03-20 -# 1490867613915828224 2023-03-20 -# 1491195742123397124 2023-03-20 -# 1492604168145539072 2023-03-20 -# 1493392149664219138 2023-03-20 -# 1493394108014292993 2023-03-20 -# 1545351225293426688 2023-03-20 -# 1545352592884084736 2023-03-20 -# 1545354510515654656 2023-03-20 -# 1545552756773208066 2023-03-20 -# 1545562635650957312 2023-03-20 -# 1546328834559340544 2023-03-20 -# 1507066475638673422 2023-03-20 -# 1506863869901168642 2023-03-20 -# 1214737620749578240 2023-03-20 -# 1165866976192823297 2023-03-20 -# 1165866977472024576 2023-03-20 -# 1165866977715347456 2023-03-20 -# 1205742630467801088 2023-01-01 -# 1205743596785127424 2023-03-20 -# 1205744131294654466 2023-03-20 -# 1205744430386315265 2023-03-20 -# 1237600624646078464 2023-03-20 -# 1237603606448058371 2023-03-20 -# 1237613895675596800 2023-03-20 -# 1290243278814683137 2023-01-01 -# 1290243331629318144 2023-03-20 -# 1290243510193369089 2023-03-20 -# 1323147415168323586 2023-03-20 -# 1323147843398324225 2023-03-20 -# 1323147856828510208 2023-03-20 -# 1414845791944937476 2023-03-20 -# 1414845844692504611 2023-03-20 -# 1414849131655450626 2023-03-20 -# 1405494022446010375 2023-03-20 -# 1152523848060850177 2023-03-20 -# 1587724357496815616 2023-03-20 -# 1589536631324692480 2023-03-20 -# 1589524401170833409 2023-03-20 -# 1589531775058968576 2023-03-20 -# 1589539582399348738 2023-03-20 -# 1591995159901663232 2023-03-20 -# 1589791076709171201 2023-03-20 -# 1582926739684339712 2023-03-20 -# 1582922712166825986 2023-03-20 -# 1582927907206631425 2023-03-20 -# 1582925071546732544 2023-03-20 - diff --git a/queue_from_forever_ago.txt b/queue_from_forever_ago.txt deleted file mode 100644 index 6685e5c..0000000 --- a/queue_from_forever_ago.txt +++ /dev/null @@ -1,1264 +0,0 @@ -# 1283657064410017793 1664344489.385418 -# 1283656034305769472 1664344547.444494 -# 1283653858510598144 1664344620.706429 -# 1283650008835743744 1664344670.144758 -# 1283646922406760448 1664344842.620527 -# 1363705980261855232 1664344873.094543 -# 1409783149211443200 1664344919.224187 -# 1409817096523968513 1664344939.072624 -# 1409784760805650436 1664344961.913775 -# 1409819816194576394 1664344974.898479 -# 1409817941705515015 1664344996.702392 -# 1536579341332516864 1664345001.30843 -# 1536577295632441344 1664345016.625882 -# 1536576325296996352 1664345032.000744 -# 1536575088996524032 1664345045.383927 -# 1397148959798226945 -1 -# 1540204458042621952 1664345170.513925 -# 1234752200145899520 1664345336.03517 -# 1234753886520393729 1664345513.866462 -# 1235180878449397764 1664345748.475805 -# 1328275136575799297 1664345847.417006 -# 1328277233492844544 1664346153.38431 -# 1328277750000492545 1664346228.777408 -# 1486629076005634049 1664346255.562165 -# 1486633489101307907 1664346300.375388 -# 1486636197908602880 1664346349.770673 -# 1390637197167038464 1664346531.48698 -# 1390620618001838086 1664346708.662696 -# 1390209302120394754 1664346908.164574 -# 1413339084076978179 1664347098.31715 -# 1413318241804439552 1664347148.634921 -# 1413326894435602434 1664347258.397339 -# 1437952405283426310 1664347402.436492 -# 1437963160544284675 1664347556.027124 -# 1437959162651156484 1664347764.128131 -# 1437961007029227520 1664347963.256051 -# 1465851881180348425 1664348015.851514 -# 1465850835951357955 1664348090.015377 -# 1465851188562345985 1664348141.651619 -# 1465851243167895554 1664348217.54261 -# 1465858739970273281 1664348250.297669 -# 1490867613915828224 1664348286.517718 -# 1491195742123397124 1664348338.923129 -# 1492604168145539072 1664348467.913091 -# 1493392149664219138 1664348548.797566 -# 1493394108014292993 1664348567.365132 -# 1545351225293426688 1664348602.592192 -# 1545352592884084736 1664348614.929205 -# 1545354510515654656 1664348639.508903 -# 1545552756773208066 1664348663.712282 -# 1545562635650957312 1664348673.912508 -# 1546328834559340544 1664348703.223831 -# 1507066475638673422 1664348709.378359 -# 1506863869901168642 1664348711.638345 -# 1214737620749578240 1664348731.025378 -# 1165866976192823297 1664349053.894626 -# 1165866977472024576 1664349262.325079 -# 1165866977715347456 1664349656.243963 -# 1205742630467801088 1664349849.238834 -# 1205743596785127424 1664350176.676804 -# 1205744131294654466 1664350296.492325 -# 1205744430386315265 1664350633.261558 -# 1237600624646078464 1664350828.802447 -# 1237603606448058371 1664350979.328163 -# 1237613895675596800 1664351433.58534 -# 1290243278814683137 1664351534.743952 -# 1290243331629318144 1664351687.473423 -# 1290243510193369089 1664351856.258976 -# 1323147415168323586 1664351965.114836 -# 1323147843398324225 1664352019.889442 -# 1323147856828510208 1664352193.249363 -# 1414845791944937476 1664352310.485566 -# 1414845844692504611 1664352353.966641 -# 1414849131655450626 1664352505.157937 -# 1405494022446010375 1664352529.72794 -# 1152523848060850177 1664352543.082985 - -1392498434620461063 1390620618001838086 1620832472.0 r 1328277233492844544 -1392499031478128651 1328277233492844544 1620832615.0 r 1390620618001838086 -1392499314979753988 1390637197167038464 1620832682.0 r 1328277233492844544 -1392499721818710016 1328277233492844544 1620832779.0 r 1390620618001838086 -1392500044440301577 1328277233492844544 1620832856.0 r 1390637197167038464 -1392500200216879108 1390620618001838086 1620832893.0 r 1328277233492844544 -1392500232181612550 1328277233492844544 1620832901.0 r 1390209302120394754 -1392500422560993280 1390209302120394754 1620832946.0 r 1328277233492844544 -1392500439417909249 1328277233492844544 1620832950.0 r 1390620618001838086 -1392500842381692934 1390637197167038464 1620833046.0 r 1328277233492844544 -1392502069802717185 1390620618001838086 1620833339.0 r 1328277233492844544 -1392506582190084099 1235180878449397764 1620834415.0 r 1390209302120394754 -1392508859005038597 1390209302120394754 1620834958.0 r 1235180878449397764 -1392509840241565706 1328277233492844544 1620835192.0 r 1390637197167038464 -1392514447034380290 1328277233492844544 1620836290.0 r 1390620618001838086 -1392668767268782084 1390620618001838086 1620873083.0 r 1328277233492844544 -1392669847645921280 1328277233492844544 1620873340.0 r 1390620618001838086 -1392872144275656709 1390637197167038464 1620921572.0 r 1328277233492844544 -1393217500993654797 1328277233492844544 1621003911.0 r 1390637197167038464 -1393218917661880320 1390637197167038464 1621004249.0 r 1328277233492844544 -1393221102751596548 1390637197167038464 1621004770.0 r 1235180878449397764 -1393241526050988036 1390637197167038464 1621009639.0 m 1328277233492844544 r 1317313010751295491 -1394128061776502786 1390637197167038464 1621221006.0 r 1328277233492844544 -1394130857862959108 1328277233492844544 1621221672.0 r 1390637197167038464 -1394149076782895106 1328277233492844544 1621226016.0 r 1390637197167038464 -1394149345738506240 1390637197167038464 1621226080.0 r 1328277233492844544 -1394153099229597697 1390620618001838086 1621226975.0 r 1328277233492844544 -1394885763490324485 1390620618001838086 1621401656.0 r 1328277233492844544 -1394886589654863875 1328277233492844544 1621401853.0 r 1390620618001838086 -1394993371182211076 1390637197167038464 1621427312.0 r 1328277233492844544 -1395017721935392777 1328277233492844544 1621433117.0 r 1390637197167038464 -1395086304287858689 1390637197167038464 1621449469.0 r 1283653858510598144 -1395086356574097411 1390620618001838086 1621449481.0 r 1283653858510598144 -1395453335868743691 1390637197167038464 1621536976.0 r 1283656034305769472 -1395673752373895169 1390209302120394754 1621589527.0 r 1328277233492844544 -1396863607124733952 1390637197167038464 1621873211.0 r 1328277233492844544 -1396863832421634060 1328277233492844544 1621873264.0 r 1390637197167038464 -1397666228303507461 1390637197167038464 1622064570.0 r 1283653858510598144 -1397667774219042821 1390620618001838086 1622064939.0 r 1283653858510598144 -1398076426180456449 1390209302120394754 1622162369.0 r 1283653858510598144 -1398327133655732228 1390620618001838086 1622222142.0 r 1328277233492844544 -1398328126925533185 1328277233492844544 1622222379.0 r 1390620618001838086 -1398329612699058179 1390637197167038464 1622222734.0 r 1328277233492844544 -1398330004837048320 1328277233492844544 1622222827.0 r 1390637197167038464 -1398331702368407553 1390637197167038464 1622223232.0 r 1328277233492844544 -1398348012548616195 1390209302120394754 1622227120.0 r 1328277233492844544 -1398513913289871361 1328277233492844544 1622266674.0 r 1390620618001838086 -1399026596996993034 1390209302120394754 1622388908.0 r 1283653858510598144 -1399961333781569537 1390209302120394754 1622611766.0 r 1328277233492844544 -1400097959996428290 1390637197167038464 1622644340.0 r 1283653858510598144 -1400100373776388108 1283653858510598144 1622644916.0 r 1390637197167038464 -1400526700618739715 1328277233492844544 1622746560.0 r 1390620618001838086 -1400680089717809154 1390620618001838086 1622783131.0 r 1328277233492844544 -1400864593719746562 1390637197167038464 1622827120.0 r 1283646922406760448 -1400876827258855426 1283646922406760448 1622830037.0 r 1390637197167038464 -1400882558754246668 1390637197167038464 1622831403.0 r 1283646922406760448 -1400926770849746947 1390209302120394754 1622841944.0 r 1328277233492844544 -1400948062927917056 1390620618001838086 1622847021.0 r 1283646922406760448 -1400967481880039424 1328277233492844544 1622851651.0 r 1390209302120394754 -1401396704721653762 1390620618001838086 1622953985.0 r 1283656034305769472 -1401409065092141058 1390209302120394754 1622956932.0 r 1283656034305769472 -1403062196062490626 1390209302120394754 1623351069.0 r 1283646922406760448 -1405568101266120708 1390620618001838086 1623948524.0 r 1328277233492844544 -1405569022066782210 1328277233492844544 1623948743.0 r 1390620618001838086 -1409975034941251586 1390637197167038464 1624999219.0 r 1283656034305769472 -1410083117319262211 1390637197167038464 1625024987.0 r 1328277233492844544 -1410086066934083587 1328277233492844544 1625025691.0 r 1390637197167038464 -1410212724391518208 1390637197167038464 1625055888.0 r 1328277233492844544 -1410994770604957703 1390637197167038464 1625242342.0 r 1283653858510598144 -1413542506441187336 1283646922406760448 1625849770.0 r 1390620618001838086 -1413562401518141442 1390637197167038464 1625854513.0 m 1390620618001838086 r 1283646922406760448 -1413645247251423243 1390620618001838086 1625874265.0 r 1283646922406760448 -1414684778490650632 1283646922406760448 1626122109.0 r 1390637197167038464 -1414685608707043334 1390637197167038464 1626122307.0 r 1283646922406760448 -1414968067071877124 1390637197167038464 1626189650.0 r 1328277233492844544 -1414969539993899008 1328277233492844544 1626190001.0 r 1390637197167038464 -1414972012171251713 1390637197167038464 1626190591.0 r 1328277233492844544 -1415502066789781505 1390620618001838086 1626316966.0 r 1235180878449397764 -1415502506684190720 1390209302120394754 1626317071.0 r 1235180878449397764 -1415506819238154244 1235180878449397764 1626318099.0 r 1390620618001838086 -1415507439391150083 1235180878449397764 1626318247.0 r 1390209302120394754 -1415508048098041857 1390209302120394754 1626318392.0 r 1235180878449397764 -1415511038196211717 1235180878449397764 1626319105.0 r 1390209302120394754 -1415518652472909833 1390209302120394754 1626320920.0 r 1235180878449397764 -1417443183689039874 1328277233492844544 1626779764.0 r 1390620618001838086 -1417945088559222788 1413318241804439552 1626899427.0 r 1328277233492844544 -1418053738640691203 1390209302120394754 1626925332.0 r 1235180878449397764 -1418057491213012993 1235180878449397764 1626926226.0 r 1390209302120394754 -1418380001204965382 1283646922406760448 1627003119.0 r 1390637197167038464 -1418586479345999874 1390637197167038464 1627052347.0 r 1283646922406760448 -1418627730191491072 1328277750000492545 1627062182.0 r 1390637197167038464 -1418629400690450432 1328277233492844544 1627062580.0 r 1390637197167038464 -1418634660138131456 1390637197167038464 1627063834.0 r 1328277750000492545 -1418634737367855111 1390637197167038464 1627063853.0 r 1328277233492844544 -1419059449382281218 1390209302120394754 1627165112.0 m 777487884 1442894217164963842 1049054859520090113 762107568244330496 1328277233492844544 r 1181015512408457216 -1419806052430893057 1390637197167038464 1627343116.0 r 1283646922406760448 -1419851570791936003 1328277233492844544 1627353968.0 r 1413318241804439552 -1419879898655563778 1413318241804439552 1627360722.0 r 1328277233492844544 -1419880286637101058 1328277233492844544 1627360815.0 r 1413318241804439552 -1420072555600392194 1283646922406760448 1627406655.0 r 1390637197167038464 -1420096413061599235 1390637197167038464 1627412343.0 r 1283646922406760448 -1420448147373920260 1328277233492844544 1627496203.0 r 1390637197167038464 -1420885470804860938 1283646922406760448 1627600469.0 r 1390637197167038464 -1420890032072708100 1390637197167038464 1627601557.0 r 1283646922406760448 -1421336659782782980 1390637197167038464 1627708041.0 r 1328277750000492545 -1422018630611607553 1390209302120394754 1627870636.0 r 1283653858510598144 -1422031214806925316 1283653858510598144 1627873636.0 r 1390209302120394754 -1423019136691621898 1390209302120394754 1628109175.0 r 1283650008835743744 -1424421743276527620 1413326894435602434 1628443582.0 r 1328277233492844544 -1424422679516311552 1328277233492844544 1628443805.0 r 1413326894435602434 -1424960958477205510 1328277233492844544 1628572141.0 r 1413318241804439552 -1424964240385921036 1413318241804439552 1628572924.0 r 1328277233492844544 -1424965832858247174 1328277233492844544 1628573303.0 r 1413318241804439552 -1424966754808344577 1413318241804439552 1628573523.0 r 1328277233492844544 -1425146117935845388 1413318241804439552 1628616287.0 r 1328277233492844544 -1425146722641059841 1328277233492844544 1628616431.0 r 1413318241804439552 -1425153372366295042 1390637197167038464 1628618016.0 r 1328277233492844544 -1425871620736905219 1328277233492844544 1628789260.0 r 1390637197167038464 -1425873427706093571 1390637197167038464 1628789691.0 r 1328277233492844544 -1427291764495462402 1390620618001838086 1629127849.0 r 1328277233492844544 -1427291871940734978 1328277233492844544 1629127874.0 r 1390620618001838086 -1427417313154830338 1283646922406760448 1629157782.0 r 1390637197167038464 -1427420018040623105 1390637197167038464 1629158427.0 r 1283646922406760448 -1427712197346701314 1390637197167038464 1629228088.0 r 1283646922406760448 -1428210248129650693 1413326894435602434 1629346832.0 r 1328277233492844544 -1428211914098675717 1328277233492844544 1629347229.0 r 1413326894435602434 -1428436854609793031 1413318241804439552 1629400859.0 r 1328277233492844544 -1428805620447301635 1390637197167038464 1629488780.0 r 1283656034305769472 -1428808007052414979 1390637197167038464 1629489349.0 r 1409784760805650436 -1428809934121037824 1409784760805650436 1629489808.0 r 1390637197167038464 -1428810997322723331 1390637197167038464 1629490062.0 r 1409784760805650436 -1428820040942460932 1234752200145899520 1629492218.0 r 1390637197167038464 -1428820551334699009 1234752200145899520 1629492340.0 r 1390620618001838086 -1428821169042505736 1390637197167038464 1629492487.0 r 1234752200145899520 -1428821704822960130 1390620618001838086 1629492615.0 r 1234752200145899520 -1428821848850980864 1234752200145899520 1629492649.0 r 1390637197167038464 -1428915861868318725 1413339084076978179 1629515064.0 r 1409784760805650436 -1429139188109103106 1409784760805650436 1629568309.0 r 1413339084076978179 -1429357933939150851 1234752200145899520 1629620462.0 r 1413339084076978179 -1429539433745891340 1390620618001838086 1629663735.0 r 1409784760805650436 -1429545625998303243 1390209302120394754 1629665211.0 r 1409784760805650436 -1429553037375275010 1409784760805650436 1629666978.0 r 1390209302120394754 -1429553192115654660 1409784760805650436 1629667015.0 r 1390620618001838086 -1429566953602043906 1413326894435602434 1629670296.0 r 1409784760805650436 -1429821972502835202 1234752200145899520 1629731097.0 r 1390620618001838086 -1429823329658052610 1328277233492844544 1629731421.0 r 1390637197167038464 -1429827361068003334 1390637197167038464 1629732382.0 r 1328277233492844544 -1429838997832929281 1234752200145899520 1629735156.0 r 1390637197167038464 -1429839725762777090 1234752200145899520 1629735330.0 r 1390209302120394754 -1429841039595085831 1390637197167038464 1629735643.0 r 1234752200145899520 -1429841530055843841 1234752200145899520 1629735760.0 r 1390637197167038464 -1429843403299229697 1390637197167038464 1629736207.0 r 1234752200145899520 -1429853008884506624 1234752200145899520 1629738497.0 r 1390637197167038464 -1429868855871119368 1390637197167038464 1629742275.0 r 1234752200145899520 -1429878700682747907 1390209302120394754 1629744622.0 r 1234752200145899520 -1430358570386608132 1235180878449397764 1629859032.0 r 1390209302120394754 -1430358716918878212 1235180878449397764 1629859067.0 r 1390209302120394754 -1430363286705295361 1234753886520393729 1629860157.0 r 1390209302120394754 -1430369752011218948 1390209302120394754 1629861698.0 r 1235180878449397764 -1430369820030251008 1390209302120394754 1629861714.0 r 1234753886520393729 -1430427743628537859 1390620618001838086 1629875524.0 r 1234752200145899520 -1430428219044294657 1234752200145899520 1629875638.0 r 1390620618001838086 -1430431944190271490 1234752200145899520 1629876526.0 r 1390620618001838086 -1430432695088209922 1390620618001838086 1629876705.0 r 1234752200145899520 -1430433052879036421 1234752200145899520 1629876790.0 r 1390620618001838086 -1430433760131133442 1390620618001838086 1629876959.0 r 1234752200145899520 -1430434121155706882 1234752200145899520 1629877045.0 r 1390620618001838086 -1430482670157393924 1235180878449397764 1629888620.0 r 1413339084076978179 -1430530034016555009 1413339084076978179 1629899912.0 r 1235180878449397764 -1430563514741968903 1234752200145899520 1629907895.0 r 1413339084076978179 -1430628192033837062 1390637197167038464 1629923315.0 r 1283646922406760448 -1430635964490162177 1234752200145899520 1629925168.0 r 1390209302120394754 -1430745543458787329 1413339084076978179 1629951294.0 r 1234752200145899520 -1430769976453959681 1234752200145899520 1629957119.0 r 1413339084076978179 -1430770231031468034 1234752200145899520 1629957180.0 r 1390620618001838086 -1430806821883695107 1413339084076978179 1629965904.0 r 1234752200145899520 -1430890275455471622 1390637197167038464 1629985800.0 r 1235180878449397764 -1430901268038311936 1234752200145899520 1629988421.0 r 1390620618001838086 -1430902295747710976 1390620618001838086 1629988666.0 r 1234752200145899520 -1430907791342854150 1234752200145899520 1629989977.0 r 1390620618001838086 -1430935786271219713 1390209302120394754 1629996651.0 r 1283653858510598144 -1430938775987707906 1234752200145899520 1629997364.0 r 1390209302120394754 -1430941346429288459 1390209302120394754 1629997977.0 r 1234752200145899520 -1430941739754299392 1234752200145899520 1629998071.0 r 1390209302120394754 -1430947995101503489 1234752200145899520 1629999562.0 r 1390637197167038464 -1430951957796212741 1390637197167038464 1630000507.0 r 1234752200145899520 -1431049288381915138 1283653858510598144 1630023712.0 r 1390209302120394754 -1431309312136122370 1413339084076978179 1630085707.0 r 1235180878449397764 -1431658163300757513 1328277233492844544 1630168879.0 r 1390637197167038464 -1431661788060258307 1234752200145899520 1630169743.0 r 1390209302120394754 -1431692828518883339 1390637197167038464 1630177144.0 r 1328277233492844544 -1431903409490182146 1234752200145899520 1630227350.0 r 1390620618001838086 -1432238700553859073 1413339084076978179 1630307290.0 r 1234752200145899520 -1432317233452044304 1234752200145899520 1630326014.0 r 1390637197167038464 -1432318296209113090 1390637197167038464 1630326267.0 r 1234752200145899520 -1432318470994104328 1234752200145899520 1630326309.0 r 1390637197167038464 -1432527039999795202 1234752200145899520 1630376036.0 r 1390620618001838086 -1432527353926729731 1234752200145899520 1630376110.0 r 1390620618001838086 -1432584527965212676 1390209302120394754 1630389742.0 r 1283653858510598144 -1432688320975409155 1390637197167038464 1630414488.0 r 1234752200145899520 -1432711413982715912 1234752200145899520 1630419994.0 r 1390637197167038464 -1432948564661919749 1234752200145899520 1630476535.0 r 1390620618001838086 -1432948612825190406 1234752200145899520 1630476546.0 r 1390209302120394754 -1432949083468091397 1390620618001838086 1630476659.0 r 1234752200145899520 -1432950809268916227 1234752200145899520 1630477070.0 r 1390620618001838086 -1432951672175800323 1390620618001838086 1630477276.0 r 1234752200145899520 -1433050035059499008 1234752200145899520 1630500727.0 r 1390637197167038464 -1433070947284697092 1390637197167038464 1630505713.0 r 1234752200145899520 -1433077946130960391 1234752200145899520 1630507382.0 r 1390637197167038464 -1433112419467816960 1234752200145899520 1630515601.0 r 1390620618001838086 -1433115359121338368 1413339084076978179 1630516302.0 r 1234752200145899520 -1433116794428948484 1234752200145899520 1630516644.0 r 1413339084076978179 -1433118871435636736 1234752200145899520 1630517139.0 r 1413339084076978179 -1433119112390193153 1390620618001838086 1630517197.0 r 1234752200145899520 -1433119851153747968 1413339084076978179 1630517373.0 r 1234752200145899520 -1433120083715313664 1234752200145899520 1630517428.0 r 1390620618001838086 -1433120860781514753 1234752200145899520 1630517613.0 r 1413339084076978179 -1433120940011917312 1234752200145899520 1630517632.0 m 1413339084076978179 r 1390637197167038464 -1433123504971452417 1390637197167038464 1630518244.0 m 1413339084076978179 r 1234752200145899520 -1433151327077609477 1390620618001838086 1630524877.0 r 1234752200145899520 -1433238723617951748 1234752200145899520 1630545714.0 r 1390620618001838086 -1433242064154607623 1234752200145899520 1630546511.0 m 1413339084076978179 r 1390637197167038464 -1433354769494523905 1234752200145899520 1630573382.0 r 1390209302120394754 -1433354877443317768 1234752200145899520 1630573407.0 r 1390209302120394754 -1433358008726089736 1234752200145899520 1630574154.0 r 1390209302120394754 -1433370754427539458 1390209302120394754 1630577193.0 r 1234752200145899520 -1433407854199721986 1234752200145899520 1630586038.0 r 1390637197167038464 -1433412605830434817 1390637197167038464 1630587171.0 r 1234752200145899520 -1433457396932755456 1234752200145899520 1630597850.0 r 1413326894435602434 -1433457531595153413 1413326894435602434 1630597882.0 r 1234752200145899520 -1433457876069064704 1234752200145899520 1630597964.0 r 1413326894435602434 -1433520798158016514 1390620618001838086 1630612966.0 r 1234753886520393729 -1433520940344741904 1234753886520393729 1630613000.0 r 1390620618001838086 -1433521049904234501 1390620618001838086 1630613026.0 r 1234753886520393729 -1433521343882924032 1234753886520393729 1630613096.0 r 1390620618001838086 -1433536589766926345 1234753886520393729 1630616731.0 r 1390637197167038464 -1433536844239491076 1234753886520393729 1630616792.0 r 1390637197167038464 -1433537049521446921 1390637197167038464 1630616841.0 r 1234753886520393729 -1433537498223874048 1390637197167038464 1630616948.0 r 1234753886520393729 -1433538362938322947 1234753886520393729 1630617154.0 r 1390637197167038464 -1433572264721661952 1390637197167038464 1630625237.0 r 1234753886520393729 -1433609603019730954 1234752200145899520 1630634139.0 r 1390637197167038464 -1433615192605093888 1234752200145899520 1630635471.0 r 1390620618001838086 -1433662735418462213 1234752200145899520 1630646806.0 r 1390637197167038464 -1433678166401245190 1413339084076978179 1630650485.0 r 1234752200145899520 -1433774732697128964 1390637197167038464 1630673509.0 r 1234752200145899520 -1433850123235250179 1390620618001838086 1630691483.0 r 1234753886520393729 -1434181257575948306 1390620618001838086 1630770432.0 r 1235180878449397764 -1434197938285146114 1235180878449397764 1630774409.0 r 1390620618001838086 -1434552802878148611 1390620618001838086 1630859015.0 r 1234752200145899520 -1434553059539963910 1234752200145899520 1630859076.0 r 1390620618001838086 -1434765161219383298 1390209302120394754 1630909645.0 r 1409783149211443200 -1434780249976283137 1234752200145899520 1630913243.0 r 1390209302120394754 -1434783445683773440 1390209302120394754 1630914005.0 r 1234752200145899520 -1435206114610720768 1390209302120394754 1631014777.0 r 1234752200145899520 -1435206466823094275 1234752200145899520 1631014861.0 r 1390209302120394754 -1435240395978334209 1413339084076978179 1631022950.0 r 1234752200145899520 -1435700233363669001 1390620618001838086 1631132584.0 r 1328275136575799297 -1435702298643206151 1390620618001838086 1631133076.0 r 1283646922406760448 -1435706247488102403 1283646922406760448 1631134018.0 r 1390620618001838086 -1435716795797852161 1413326894435602434 1631136533.0 r 1409784760805650436 -1435719266284695553 1409784760805650436 1631137122.0 r 1413326894435602434 -1435850106322911235 1328275136575799297 1631168316.0 r 1390620618001838086 -1435850183636656129 1390209302120394754 1631168335.0 r 1328275136575799297 -1435850489376038918 1328275136575799297 1631168408.0 r 1390209302120394754 -1436307947211182084 1390637197167038464 1631277474.0 r 1328277233492844544 -1436385698278363141 1328277233492844544 1631296011.0 r 1390637197167038464 -1436747331337027586 1234752200145899520 1631382231.0 r 1390209302120394754 -1437267913573117953 1390637197167038464 1631506348.0 r 1283646922406760448 -1437268242670706688 1283646922406760448 1631506426.0 r 1390637197167038464 -1437268463555317764 1390637197167038464 1631506479.0 r 1283646922406760448 -1437269258938032129 1283646922406760448 1631506669.0 r 1390637197167038464 -1437269580515323914 1390637197167038464 1631506745.0 r 1283646922406760448 -1437269717899689985 1283646922406760448 1631506778.0 r 1390637197167038464 -1437270025635831808 1390637197167038464 1631506851.0 r 1283646922406760448 -1437315657171968003 1390620618001838086 1631517731.0 r 1328277233492844544 -1437369336142843904 1328277233492844544 1631530529.0 r 1390620618001838086 -1437453788697096192 1328277233492844544 1631550664.0 m 1290243278814683137 1384391383545368577 r 1413318241804439552 -1437949445765742601 1390620618001838086 1631668838.0 r 1409783149211443200 -1437954151992860675 1409783149211443200 1631669960.0 r 1390620618001838086 -1438325452435767298 1390209302120394754 1631758485.0 m 1235180878449397764 r 2281401692 -1438362672773222400 1328277233492844544 1631767359.0 r 1413326894435602434 -1438379649848025090 1413326894435602434 1631771406.0 r 1328277233492844544 -1438849098791194624 1390637197167038464 1631883332.0 r 1234752200145899520 -1439134717803065344 1234752200145899520 1631951429.0 r 1390620618001838086 -1439134914473988096 1234752200145899520 1631951476.0 r 1390620618001838086 -1439135060448485377 1390620618001838086 1631951510.0 r 1234752200145899520 -1439161782162001922 1234752200145899520 1631957881.0 r 1390620618001838086 -1439162651788017665 1234752200145899520 1631958089.0 r 1413318241804439552 -1439937165543178240 1234752200145899520 1632142747.0 r 1390209302120394754 -1440673241270390785 1413326894435602434 1632318241.0 r 1328277233492844544 -1440912691242045444 1234752200145899520 1632375331.0 r 1390620618001838086 -1440912765544239111 1390620618001838086 1632375348.0 r 1234752200145899520 -1440913480551329799 1234752200145899520 1632375519.0 r 1390620618001838086 -1442718888865632256 1390620618001838086 1632805962.0 r 1234752200145899520 -1442719353086955520 1234752200145899520 1632806072.0 r 1390620618001838086 -1442762126184140801 1390620618001838086 1632816270.0 r 1234752200145899520 -1442762392782315527 1234752200145899520 1632816334.0 r 1390620618001838086 -1442764057254236160 1390620618001838086 1632816731.0 r 1234752200145899520 -1442764187399180291 1234752200145899520 1632816762.0 r 1390620618001838086 -1446083918000709632 1437952405283426310 1633608247.0 r 1234753886520393729 -1446089048037081091 1234752200145899520 1633609470.0 r 1390209302120394754 -1446089255097405444 1390209302120394754 1633609520.0 r 1234752200145899520 -1446089509267914759 1234752200145899520 1633609580.0 r 1390209302120394754 -1446095346308837377 1234753886520393729 1633610972.0 r 1437952405283426310 -1446251659793879040 1409784760805650436 1633648240.0 r 1390637197167038464 -1446266844256604163 1437952405283426310 1633651860.0 r 1409783149211443200 -1446282322135945220 1437952405283426310 1633655550.0 r 1235180878449397764 -1446289291261452289 1437952405283426310 1633657212.0 r 1234753886520393729 -1446308829218607104 1390637197167038464 1633661870.0 r 1409784760805650436 -1446375871963672593 1437952405283426310 1633677854.0 r 1234752200145899520 -1446397952809848835 1234752200145899520 1633683119.0 r 1437952405283426310 -1446398666302971926 1437952405283426310 1633683289.0 r 1328275136575799297 -1446699043988520960 1234753886520393729 1633754905.0 r 1437952405283426310 -1446699326843985925 1234753886520393729 1633754972.0 r 1390620618001838086 -1446699776649711617 1390209302120394754 1633755079.0 m 1390620618001838086 r 1234753886520393729 -1446701773050322944 1437952405283426310 1633755555.0 r 1234753886520393729 -1446991643396255751 1437952405283426310 1633824666.0 r 1234753886520393729 -1446995900786569216 1234753886520393729 1633825681.0 r 1437952405283426310 -1446996140164096001 1437952405283426310 1633825738.0 r 1234753886520393729 -1447021631482867712 1437952405283426310 1633831815.0 r 1235180878449397764 -1447313440381493250 1390637197167038464 1633901388.0 m 940478415110488064 r 1283646922406760448 -1447318371012329477 1283646922406760448 1633902564.0 m 1390637197167038464 r 1349946099537235971 -1447325050445705220 1437952405283426310 1633904156.0 m 798860390 r 1363705980261855232 -1447372602071212032 1390637197167038464 1633915493.0 m 1349946099537235971 r 1283646922406760448 -1447475333989421060 1328277750000492545 1633939987.0 r 1437952405283426310 -1447476225857949698 1437952405283426310 1633940199.0 r 1328277750000492545 -1447647647620272128 1437952405283426310 1633981069.0 m 1328275136575799297 r 1290243510193369089 -1447972860715102208 1413339084076978179 1634058606.0 r 1328277233492844544 -1447976781143756801 1390637197167038464 1634059541.0 r 1328277233492844544 -1447976941152350210 1390620618001838086 1634059579.0 r 1328277233492844544 -1447986639301529602 1328277233492844544 1634061891.0 r 1413339084076978179 -1447988461701672961 1413326894435602434 1634062326.0 r 1328277233492844544 -1447989480099495941 1328277233492844544 1634062569.0 r 1390637197167038464 -1447989864054489089 1328277233492844544 1634062660.0 r 1390620618001838086 -1447993966473801732 1328277233492844544 1634063638.0 r 1413326894435602434 -1448518254859886593 1437952405283426310 1634188638.0 r 1234753886520393729 -1448543166227828741 1234753886520393729 1634194578.0 r 1437952405283426310 -1448544022717476865 1437952405283426310 1634194782.0 r 1234753886520393729 -1448545011843301387 1328277233492844544 1634195018.0 r 1390637197167038464 -1448557140587651076 1234753886520393729 1634197909.0 r 1437952405283426310 -1448597887273734145 1234753886520393729 1634207624.0 m 1437952405283426310 r 1346828641989103617 -1448904644873306114 1390209302120394754 1634280761.0 r 1328277233492844544 -1448904926835494926 1390209302120394754 1634280828.0 r 1328277233492844544 -1448918288965124098 1328277233492844544 1634284014.0 r 1390209302120394754 -1449224630082826247 1328277233492844544 1634357051.0 r 1390620618001838086 -1449343421219442690 1409783149211443200 1634385373.0 r 1390620618001838086 -1449422053090660356 1390620618001838086 1634404121.0 r 1409783149211443200 -1449427870410059786 1390620618001838086 1634405508.0 r 1328277233492844544 -1449537803684040705 1437952405283426310 1634431718.0 r 1234753886520393729 -1449931091280863233 1437952405283426310 1634525485.0 r 1235180878449397764 -1450089512298860547 1437952405283426310 1634563255.0 r 1234753886520393729 -1450497623543009285 1235180878449397764 1634660557.0 r 1413339084076978179 -1450498114083713024 1413339084076978179 1634660674.0 r 1235180878449397764 -1450498599691816960 1235180878449397764 1634660789.0 r 1413339084076978179 -1450499675929264137 1413339084076978179 1634661046.0 r 1235180878449397764 -1450511407737237511 1413339084076978179 1634663843.0 r 1235180878449397764 -1450512754368974848 1413326894435602434 1634664164.0 m 1235180878449397764 r 1413339084076978179 -1450513261376393216 1413339084076978179 1634664285.0 m 1235180878449397764 r 1413326894435602434 -1450581102884700168 1437952405283426310 1634680460.0 r 1234752200145899520 -1450604660625186816 1283646922406760448 1634686076.0 r 1390637197167038464 -1450604699883814915 1283646922406760448 1634686086.0 r 1390637197167038464 -1450613898722304000 1390637197167038464 1634688279.0 r 1283646922406760448 -1450641416200228877 1235180878449397764 1634694839.0 m 1413326894435602434 r 1413339084076978179 -1450676369130541058 1234752200145899520 1634703173.0 r 1437952405283426310 -1450701105227255813 1437952405283426310 1634709070.0 r 1234752200145899520 -1450743279683870721 1437952405283426310 1634719126.0 r 1235180878449397764 -1450743930249613318 1235180878449397764 1634719281.0 r 1437952405283426310 -1450799547358216194 1234753886520393729 1634732541.0 r 1437952405283426310 -1450855205839331331 1413339084076978179 1634745811.0 m 1413326894435602434 r 1235180878449397764 -1450991322769367044 1437952405283426310 1634778264.0 r 1234753886520393729 -1451054218417262594 1437952405283426310 1634793259.0 r 1234753886520393729 -1451152929881313280 1437961007029227520 1634816794.0 r 1328277233492844544 -1452000309182619650 1437952405283426310 1635018825.0 r 1409784760805650436 -1452020423093149701 1409784760805650436 1635023620.0 r 1437952405283426310 -1452472533891043329 1390637197167038464 1635131412.0 r 1283646922406760448 -1452476842611515395 1390637197167038464 1635132439.0 r 1283646922406760448 -1452477416811728899 1283646922406760448 1635132576.0 r 1390637197167038464 -1452480136515932164 1390637197167038464 1635133224.0 r 1283646922406760448 -1452489316727341056 1235180878449397764 1635135413.0 r 1413339084076978179 -1452511157051215872 1413339084076978179 1635140620.0 r 1235180878449397764 -1453450128300384262 1283646922406760448 1635364489.0 r 1390637197167038464 -1453494703660453896 1390637197167038464 1635375116.0 r 1283646922406760448 -1453773365290885121 1390209302120394754 1635441554.0 r 1283653858510598144 -1454197800225415173 1437961007029227520 1635542747.0 r 1409817096523968513 -1454204180990599171 1409817096523968513 1635544269.0 r 1437961007029227520 -1454865843863658498 1390209302120394754 1635702021.0 r 1283646922406760448 -1454865962587631621 1283646922406760448 1635702050.0 r 1390209302120394754 -1454881378546372611 1437961007029227520 1635705725.0 r 1283653858510598144 -1454883449018335237 1283653858510598144 1635706219.0 r 1437961007029227520 -1454884127153459203 1437961007029227520 1635706381.0 r 1283653858510598144 -1455070751921377281 1234752200145899520 1635750875.0 r 1390620618001838086 -1455253772255571968 1390620618001838086 1635794511.0 r 1328277233492844544 -1455253870787981312 1328277233492844544 1635794534.0 r 1390620618001838086 -1455399612580253698 1390620618001838086 1635829282.0 r 1234752200145899520 -1455774098911547395 1437952405283426310 1635918566.0 r 1234752200145899520 -1455922351682453508 1328275136575799297 1635953913.0 r 1437952405283426310 -1455924484985212928 1390637197167038464 1635954421.0 m 1437952405283426310 r 1328275136575799297 -1456139132971986945 1437952405283426310 1636005597.0 r 1235180878449397764 -1456287276405755908 1437952405283426310 1636040917.0 r 1234752200145899520 -1456288051689246720 1234752200145899520 1636041102.0 r 1437952405283426310 -1456290031572819972 1390620618001838086 1636041574.0 r 1234752200145899520 -1456291227511361546 1234752200145899520 1636041859.0 r 1390620618001838086 -1457510234260443138 1437952405283426310 1636332493.0 r 1234752200145899520 -1457569478221336585 1328277750000492545 1636346618.0 r 1390637197167038464 -1457778829519249412 1390637197167038464 1636396531.0 r 1328277750000492545 -1458578892415979523 1437952405283426310 1636587281.0 r 1283653858510598144 -1458580320882774021 1437952405283426310 1636587622.0 r 1234753886520393729 -1458929019886477319 1437961007029227520 1636670758.0 r 1409819816194576394 -1459015369515933696 1328277233492844544 1636691345.0 r 1390637197167038464 -1459038426326568960 1390637197167038464 1636696843.0 r 1328277233492844544 -1459430026508648450 1390620618001838086 1636790207.0 r 1328277233492844544 -1459430123560595456 1328277233492844544 1636790231.0 r 1390620618001838086 -1459520226559733768 1437952405283426310 1636811713.0 r 1283653858510598144 -1459524999048404995 1283653858510598144 1636812851.0 r 1437952405283426310 -1460069288979714053 1437952405283426310 1636942619.0 r 1409783149211443200 -1460155267765751809 1437952405283426310 1636963118.0 r 1234753886520393729 -1460156597846503425 1234753886520393729 1636963435.0 r 1437952405283426310 -1460162458606313472 1437952405283426310 1636964833.0 r 1234753886520393729 -1460163951887478787 1234753886520393729 1636965189.0 r 1437952405283426310 -1460178943340957702 1437952405283426310 1636968763.0 r 1234753886520393729 -1460755640331214853 1437961007029227520 1637106258.0 r 1283646922406760448 -1460807239850086401 1235180878449397764 1637118561.0 r 1437961007029227520 -1460863426960498690 1437961007029227520 1637131957.0 r 1235180878449397764 -1460931785265664000 1437961007029227520 1637148255.0 r 1283646922406760448 -1461682844091269120 1283646922406760448 1637327321.0 r 1390637197167038464 -1461695366940741642 1390637197167038464 1637330307.0 r 1283646922406760448 -1462118906198327300 1413339084076978179 1637431286.0 r 1328277233492844544 -1462333984570970117 1328275136575799297 1637482565.0 r 1437952405283426310 -1462649655402156034 1437952405283426310 1637557827.0 r 1234752200145899520 -1462650378051145733 1234752200145899520 1637557999.0 r 1437952405283426310 -1462667122346274825 1437952405283426310 1637561991.0 r 1234752200145899520 -1462675768362864642 1390620618001838086 1637564053.0 r 1328277233492844544 -1462675958012559366 1413326894435602434 1637564098.0 r 1328277233492844544 -1462676089226944513 1328277233492844544 1637564129.0 r 1390620618001838086 -1462676169925410816 1328277233492844544 1637564148.0 r 1413326894435602434 -1462681304495972353 1437961007029227520 1637565372.0 r 1328277233492844544 -1462710228038959111 1437952405283426310 1637572268.0 r 1409783149211443200 -1462711607784611844 1437959162651156484 1637572597.0 r 1328277233492844544 -1462772799056093186 1390637197167038464 1637587186.0 r 1328277233492844544 -1462917783713366018 1283646922406760448 1637621753.0 r 1390637197167038464 -1463117724389986308 1437952405283426310 1637669423.0 r 1328277233492844544 -1463127362405482505 1328277233492844544 1637671721.0 r 1437952405283426310 -1463211528992157698 1328277233492844544 1637691788.0 r 1390620618001838086 -1463245859429048326 1328277233492844544 1637699973.0 r 1437961007029227520 -1463246638038847491 1437961007029227520 1637700158.0 r 1328277233492844544 -1463246820075708418 1328277233492844544 1637700202.0 r 1437961007029227520 -1463247105351471105 1283646922406760448 1637700270.0 r 1390637197167038464 -1463248238903967749 1437961007029227520 1637700540.0 r 1328277233492844544 -1463263942076243976 1390637197167038464 1637704284.0 r 1283646922406760448 -1463506497967693829 1437961007029227520 1637762114.0 r 1283653858510598144 -1464044082389561344 1437961007029227520 1637890284.0 r 1283646922406760448 -1464079881705164802 1234752200145899520 1637898819.0 r 1390620618001838086 -1464080279283376128 1390620618001838086 1637898914.0 r 1234752200145899520 -1464486887259709445 1328277233492844544 1637995857.0 r 1437952405283426310 -1464778044703318019 1235180878449397764 1638065274.0 r 1437961007029227520 -1464778410715238404 1437961007029227520 1638065362.0 r 1235180878449397764 -1464831456480399362 1328277233492844544 1638078009.0 r 1413318241804439552 -1465408621672546316 1390620618001838086 1638215616.0 r 1283646922406760448 -1465739444875579406 1437961007029227520 1638294490.0 r 1283653858510598144 -1465742711030386693 1283653858510598144 1638295269.0 r 1437961007029227520 -1465746885222678530 1390209302120394754 1638296264.0 r 1283653858510598144 -1465750645667995654 1437961007029227520 1638297160.0 r 1283653858510598144 -1465752207450542085 1328277233492844544 1638297533.0 r 1413318241804439552 -1465766955864842241 1283653858510598144 1638301049.0 r 1437961007029227520 -1465803320115802116 1437961007029227520 1638309719.0 r 1283653858510598144 -1465905967170609152 1328277233492844544 1638334192.0 r 1437961007029227520 -1465906131499462657 1437961007029227520 1638334231.0 r 1328277233492844544 -1466291310583693313 1328277233492844544 1638426065.0 r 1437961007029227520 -1466292942985965571 1437961007029227520 1638426454.0 r 1328277233492844544 -1466293502468255744 1328277233492844544 1638426588.0 r 1437961007029227520 -1466324028596133891 1390620618001838086 1638433866.0 r 1234753886520393729 -1467602737735118851 1283646922406760448 1638738734.0 r 1390637197167038464 -1467604359932526606 1390637197167038464 1638739120.0 r 1283646922406760448 -1467858040439386114 1437952405283426310 1638799602.0 r 1283653858510598144 -1467873755552567303 1328277233492844544 1638803349.0 r 1437961007029227520 -1467874058578677763 1437961007029227520 1638803422.0 r 1328277233492844544 -1467874512456720387 1328277233492844544 1638803530.0 r 1437961007029227520 -1467877826300985348 1437961007029227520 1638804320.0 r 1328277233492844544 -1468062174803341316 1437963160544284675 1638848272.0 r 1409819816194576394 -1468222582264512513 1328277233492844544 1638886516.0 r 1437961007029227520 -1468222727941201930 1437961007029227520 1638886551.0 r 1328277233492844544 -1468546046741594122 1437961007029227520 1638963636.0 r 1283653858510598144 -1468813060743872513 1437952405283426310 1639027297.0 r 1234753886520393729 -1469324934300196869 1437952405283426310 1639149337.0 r 1283657064410017793 -1469492764794523655 1235180878449397764 1639189351.0 r 1413339084076978179 -1469494820909780992 1413339084076978179 1639189841.0 r 1235180878449397764 -1469946934051762176 1328277750000492545 1639297634.0 r 1437952405283426310 -1470023971865341953 1437961007029227520 1639316001.0 r 1283656034305769472 -1470568298832146432 1283646922406760448 1639445778.0 r 1390637197167038464 -1471617531655999497 1283646922406760448 1639695935.0 r 1390637197167038464 -1471635232277282824 1390637197167038464 1639700155.0 r 1283646922406760448 -1471828958756978705 1437961007029227520 1639746343.0 r 1328277233492844544 -1471831232132509703 1328277233492844544 1639746885.0 r 1437961007029227520 -1471831355201830912 1437961007029227520 1639746915.0 r 1328277233492844544 -1472461796900585472 1390637197167038464 1639897224.0 r 1328277233492844544 -1472464143630307329 1328277233492844544 1639897783.0 r 1390637197167038464 -1472540237758881794 1437961007029227520 1639915925.0 r 1283653858510598144 -1473544506607902724 1465850835951357955 1640155362.0 r 1328277233492844544 -1473548798374805504 1328277233492844544 1640156385.0 r 1465850835951357955 -1473549938764263426 1465850835951357955 1640156657.0 r 1328277233492844544 -1473556203401740289 1328277233492844544 1640158150.0 r 1465850835951357955 -1473556351695695877 1465850835951357955 1640158186.0 r 1328277233492844544 -1473808463784513540 1283646922406760448 1640218294.0 r 1390637197167038464 -1474557347167014913 1409783149211443200 1640396842.0 r 1390620618001838086 -1474558433978458118 1390620618001838086 1640397101.0 r 1409783149211443200 -1474561992492929025 1409783149211443200 1640397949.0 r 1390620618001838086 -1475206211146596355 1437961007029227520 1640551543.0 r 1283657064410017793 -1475274613445824513 1283646922406760448 1640567851.0 r 1390620618001838086 -1475274801065431042 1390620618001838086 1640567896.0 r 1283646922406760448 -1475275087981027333 1283646922406760448 1640567964.0 r 1390620618001838086 -1475276523879940096 1283646922406760448 1640568307.0 r 1390620618001838086 -1475286299347140613 1283646922406760448 1640570637.0 r 1390637197167038464 -1475362358184849410 1390637197167038464 1640588771.0 r 1283646922406760448 -1475446261436370945 1283646922406760448 1640608775.0 r 1390637197167038464 -1475864901994401793 1437961007029227520 1640708587.0 r 1283653858510598144 -1475877758207393792 1283653858510598144 1640711652.0 r 1437961007029227520 -1477544494258081799 1437961007029227520 1641109033.0 r 1283653858510598144 -1477872156792872961 1328275136575799297 1641187154.0 r 1390209302120394754 -1478711000555786240 1437961007029227520 1641387150.0 r 1328277233492844544 -1478711314474176512 1328277233492844544 1641387225.0 r 1437961007029227520 -1478967424544301057 1390637197167038464 1641448286.0 r 1283646922406760448 -1479263175925112833 1328277233492844544 1641518799.0 r 1413318241804439552 -1479943526792187905 1390620618001838086 1641681007.0 r 1283646922406760448 -1479944820613337093 1283646922406760448 1641681315.0 r 1390620618001838086 -1479945154194780162 1390209302120394754 1641681395.0 r 1283646922406760448 -1479945561356832773 1283646922406760448 1641681492.0 r 1390209302120394754 -1479954219377598464 1437952405283426310 1641683556.0 r 1283646922406760448 -1479955264950439936 1283646922406760448 1641683806.0 r 1437952405283426310 -1479958770746855430 1437952405283426310 1641684641.0 r 1283646922406760448 -1480067222185791490 1328277233492844544 1641710498.0 r 1390637197167038464 -1480244192077131784 1437952405283426310 1641752691.0 r 1235180878449397764 -1480303880021889035 1283646922406760448 1641766922.0 r 1437963160544284675 -1480308277074436096 1437963160544284675 1641767970.0 r 1283646922406760448 -1480884946411704333 1437952405283426310 1641905459.0 r 1235180878449397764 -1482622527172235265 1390620618001838086 1642319730.0 r 1409783149211443200 -1482627917436043267 1409783149211443200 1642321016.0 r 1390620618001838086 -1484094248190464001 1437961007029227520 1642670616.0 r 1409783149211443200 -1484144239197917188 1283653858510598144 1642682535.0 r 1465851243167895554 -1484148783948648456 1465851243167895554 1642683618.0 r 1283653858510598144 -1485572024286302208 1465851881180348425 1643022945.0 r 1283653858510598144 -1485576182682689537 1283653858510598144 1643023937.0 r 1465851881180348425 -1485659923107225601 1437961007029227520 1643043902.0 r 1328277233492844544 -1485660333943701506 1328277233492844544 1643044000.0 r 1437961007029227520 -1485660614181666819 1437961007029227520 1643044067.0 r 1328277233492844544 -1485661155154890759 1328277233492844544 1643044196.0 r 1437961007029227520 -1485665466341371908 1465851188562345985 1643045224.0 r 1328277233492844544 -1485666262735007746 1328277233492844544 1643045414.0 r 1465851188562345985 -1486034076389842944 1283646922406760448 1643133107.0 r 1390637197167038464 -1486102073154408458 1390637197167038464 1643149319.0 r 1283646922406760448 -1486132327705153536 1283646922406760448 1643156532.0 m 1390637197167038464 r 15871080 -1486164094394720258 1437952405283426310 1643164106.0 r 1234752200145899520 -1486164410590990336 1234752200145899520 1643164181.0 r 1437952405283426310 -1486169274507046912 1390637197167038464 1643165341.0 m 1283646922406760448 r 15871080 -1487210462550843394 1283646922406760448 1643413579.0 r 1390637197167038464 -1487216468114960384 1390637197167038464 1643415011.0 r 1283646922406760448 -1487296216094605315 1328277233492844544 1643434025.0 r 1437952405283426310 -1487385794398539778 1437952405283426310 1643455382.0 r 1328277233492844544 -1487386383475896325 1328277233492844544 1643455522.0 r 1437952405283426310 -1487581310281400320 1409783149211443200 1643501996.0 r 1390620618001838086 -1487582151109455872 1390620618001838086 1643502197.0 r 1409783149211443200 -1487706138611052544 1283653858510598144 1643531758.0 r 1465851188562345985 -1488162064736702474 1465851188562345985 1643640459.0 r 1283653858510598144 -1488176746784440322 1283653858510598144 1643643960.0 r 1465851188562345985 -1488180468885372929 1465851188562345985 1643644847.0 r 1283653858510598144 -1489265848988880900 1283653858510598144 1643903622.0 r 1465850835951357955 -1489316252841558023 1465850835951357955 1643915639.0 r 1283653858510598144 -1489424244312350720 1390209302120394754 1643941386.0 r 1283653858510598144 -1489473785233395714 1437961007029227520 1643953198.0 r 1283653858510598144 -1489482433124732934 1283653858510598144 1643955259.0 r 1437961007029227520 -1489739460036210692 1437961007029227520 1644016539.0 r 1283657064410017793 -1490455082156150785 1437952405283426310 1644187157.0 r 1283656034305769472 -1490738443307073539 1437952405283426310 1644254716.0 r 1234752200145899520 -1491186842460655616 1390637197167038464 1644361622.0 r 1283646922406760448 -1491232968865882113 1437952405283426310 1644372620.0 r 1283653858510598144 -1491887131723239429 1437952405283426310 1644528584.0 r 1283646922406760448 -1492331873925795846 1465851881180348425 1644634619.0 r 1283653858510598144 -1492543548662730754 1390637197167038464 1644685086.0 r 1235180878449397764 -1492891648761958407 1413339084076978179 1644768080.0 r 1328277233492844544 -1492915130363158530 1328277233492844544 1644773678.0 r 1413339084076978179 -1492993053707149314 1283646922406760448 1644792257.0 r 1437963160544284675 -1492993940265570310 1437963160544284675 1644792468.0 r 1283646922406760448 -1493203186378756100 1437952405283426310 1644842356.0 r 1234753886520393729 -1493334718015848451 1283646922406760448 1644873716.0 r 1390209302120394754 -1493490774708785153 1390209302120394754 1644910923.0 r 1234753886520393729 -1493541179585089538 1390209302120394754 1644922940.0 r 1234753886520393729 -1493850230344720388 1390209302120394754 1644996623.0 r 1283646922406760448 -1494917665487474692 1390620618001838086 1645251120.0 r 1235180878449397764 -1494923779138781188 1235180878449397764 1645252577.0 r 1390620618001838086 -1494926067865370626 1390620618001838086 1645253123.0 r 1235180878449397764 -1495145800225202181 1437952405283426310 1645305511.0 r 1234753886520393729 -1495147426914926592 1234753886520393729 1645305899.0 r 1437952405283426310 -1495170341966127108 1437952405283426310 1645311363.0 r 1234753886520393729 -1495563588311949318 1283646922406760448 1645405120.0 r 1390637197167038464 -1495563891941822475 1390637197167038464 1645405192.0 r 1283646922406760448 -1495564079720812549 1283646922406760448 1645405237.0 r 1390637197167038464 -1495566161030823941 1390637197167038464 1645405733.0 r 1283646922406760448 -1495568142445256708 1283646922406760448 1645406206.0 r 1390637197167038464 -1496005091505156099 1413326894435602434 1645510382.0 r 1234753886520393729 -1496420263034081282 1437961007029227520 1645609367.0 r 1283653858510598144 -1496441522669371392 1283653858510598144 1645614436.0 r 1437961007029227520 -1496542724060762115 1390620618001838086 1645638564.0 r 1283646922406760448 -1497104971766153222 1493392149664219138 1645772614.0 r 1328277233492844544 -1497105994245369860 1328277233492844544 1645772858.0 r 1493392149664219138 -1497110600039776285 1493392149664219138 1645773956.0 r 1328277233492844544 -1497111602730258436 1328277233492844544 1645774195.0 r 1493392149664219138 -1497112087096614919 1493392149664219138 1645774311.0 r 1328277233492844544 -1497112251945598976 1328277233492844544 1645774350.0 r 1493392149664219138 -1497112735057862661 1493392149664219138 1645774465.0 r 1328277233492844544 -1497113066173243392 1328277233492844544 1645774544.0 r 1493392149664219138 -1497115119251501056 1328277233492844544 1645775034.0 r 1493392149664219138 -1497115536077078529 1437961007029227520 1645775133.0 m 1328277233492844544 r 1493392149664219138 -1497115876532834308 1493392149664219138 1645775214.0 r 1328277233492844544 -1497116513316524036 1328277233492844544 1645775366.0 m 1493392149664219138 r 1437961007029227520 -1497116642114998274 1437961007029227520 1645775397.0 m 1493392149664219138 r 1328277233492844544 -1497116896541700097 1328277233492844544 1645775457.0 r 1493392149664219138 -1497117131657576450 1328277233492844544 1645775513.0 r 1437961007029227520 -1497117709846425601 1493392149664219138 1645775651.0 r 1328277233492844544 -1497117727273672719 1437961007029227520 1645775655.0 r 1328277233492844544 -1497119593273638915 1328277233492844544 1645776100.0 r 1493392149664219138 -1497120264626266120 1493392149664219138 1645776260.0 r 1328277233492844544 -1497121111883927560 1493392149664219138 1645776462.0 m 1328277233492844544 r 1454005013043236865 -1497243342391812100 1493392149664219138 1645805604.0 m 1328277233492844544 r 2862272008 -1497247278809366528 1328277233492844544 1645806543.0 r 1493392149664219138 -1497577568551137280 1437952405283426310 1645885290.0 r 1409783149211443200 -1498266823132917762 1283646922406760448 1646049621.0 r 1390209302120394754 -1499175025513046020 1465851881180348425 1646266154.0 r 1283653858510598144 -1499206542091972608 1328277233492844544 1646273668.0 r 1437961007029227520 -1499209283363352579 1437961007029227520 1646274321.0 r 1328277233492844544 -1499210793413320705 1328277233492844544 1646274681.0 r 1437961007029227520 -1499493705371291650 1465851881180348425 1646342133.0 r 1283653858510598144 -1499506282763415586 1283653858510598144 1646345131.0 r 1465851881180348425 -1499977688953798658 1234752200145899520 1646457523.0 r 1390209302120394754 -1499984485722509312 1390209302120394754 1646459144.0 r 1234752200145899520 -1500043774809968642 1234753886520393729 1646473280.0 r 1390209302120394754 -1500044906643050500 1390209302120394754 1646473549.0 r 1234753886520393729 -1501738068667248647 1437952405283426310 1646877231.0 r 1235180878449397764 -1502294718863925252 1235180878449397764 1647009946.0 r 1437961007029227520 -1502300104233046026 1437961007029227520 1647011230.0 r 1235180878449397764 -1502443603368546313 1437952405283426310 1647045443.0 r 1328277750000492545 -1502449558470672387 1437952405283426310 1647046863.0 r 1283646922406760448 -1502692832485994496 1328277750000492545 1647104864.0 r 1437952405283426310 -1504902646662062085 1437952405283426310 1647631725.0 r 1234753886520393729 -1506327236705759243 1390620618001838086 1647971374.0 r 1283646922406760448 -1506327325780189184 1390620618001838086 1647971395.0 r 1283646922406760448 -1506328493113626629 1283646922406760448 1647971673.0 r 1390620618001838086 -1506336269093289991 1390620618001838086 1647973527.0 r 1283646922406760448 -1506704921789444099 1437959162651156484 1648061421.0 r 1328277233492844544 -1506705184650854400 1328277233492844544 1648061483.0 r 1437959162651156484 -1507007011656929285 1437961007029227520 1648133445.0 r 1328277233492844544 -1507009928401088524 1390637197167038464 1648134140.0 r 1328277233492844544 -1507033351399645186 1437959162651156484 1648139724.0 r 1328277233492844544 -1507148847591702533 1465850835951357955 1648167261.0 r 1328277233492844544 -1507150538311626752 1328277233492844544 1648167664.0 r 1437961007029227520 -1507152188702957573 1437952405283426310 1648168057.0 r 1328277233492844544 -1507154613220556801 1328277233492844544 1648168636.0 r 1390637197167038464 -1507166912819896321 1328277233492844544 1648171568.0 r 1437959162651156484 -1507175683272847361 1328277233492844544 1648173659.0 r 1465850835951357955 -1507178488117178373 1328277233492844544 1648174328.0 r 1437952405283426310 -1507202818515652611 1437961007029227520 1648180129.0 r 1328277233492844544 -1507596181400133635 1437952405283426310 1648273914.0 r 1234753886520393729 -1509567828763938819 1235180878449397764 1648743991.0 r 1437952405283426310 -1509568329941409809 1437952405283426310 1648744110.0 r 1235180878449397764 -1509933321610006528 1283646922406760448 1648831131.0 r 1390637197167038464 -1509933932015456261 1390637197167038464 1648831277.0 r 1283646922406760448 -1509935036929351708 1283646922406760448 1648831540.0 r 1390637197167038464 -1510248957393448964 1437959162651156484 1648906385.0 r 1328277233492844544 -1510249282233843720 1437959162651156484 1648906462.0 m 36071768 r 1409783149211443200 -1510815279386963978 1390637197167038464 1649041406.0 r 1283646922406760448 -1511000641287639043 1465851188562345985 1649085600.0 r 1283653858510598144 -1511001173137973257 1437961007029227520 1649085727.0 r 1283653858510598144 -1512527205301202944 1437961007029227520 1649449561.0 r 1283653858510598144 -1512691861286236164 1390209302120394754 1649488818.0 r 1328275136575799297 -1514297510977810436 1437961007029227520 1649871635.0 r 1283653858510598144 -1515593353257127936 1328277233492844544 1650180588.0 r 1390637197167038464 -1516063099022888969 1437952405283426310 1650292584.0 r 1235180878449397764 -1516914151410606080 1437959162651156484 1650495491.0 r 1328277233492844544 -1517279623444414464 1283646922406760448 1650582626.0 r 1390637197167038464 -1517330769332613120 1390637197167038464 1650594820.0 r 1283646922406760448 -1517484570257637376 1437952405283426310 1650631489.0 r 1283646922406760448 -1517642964654297096 1283646922406760448 1650669253.0 r 1390637197167038464 -1519470667204374529 1437952405283426310 1651105012.0 r 1328275136575799297 -1519711781865897985 1390637197167038464 1651162498.0 m 15871080 r 1283646922406760448 -1521559173653319681 1390637197167038464 1651602950.0 r 1283646922406760448 -1521559297741762560 1283646922406760448 1651602980.0 r 1390637197167038464 -1523701030415966208 1437952405283426310 1652113609.0 r 1234753886520393729 -1523701296196628482 1234753886520393729 1652113672.0 r 1437952405283426310 -1523701585276198912 1437952405283426310 1652113741.0 r 1234753886520393729 -1524726976585650179 1283646922406760448 1652358213.0 r 1390637197167038464 -1524933552982335491 1437952405283426310 1652407465.0 r 1328275136575799297 -1524935738655375361 1328275136575799297 1652407986.0 r 1437952405283426310 -1524958512165847040 1437963160544284675 1652413416.0 r 1409817941705515015 -1524983994277691392 1409817941705515015 1652419491.0 r 1437963160544284675 -1525533107306303489 1437952405283426310 1652550410.0 r 1486629076005634049 -1525536905336586240 1437959162651156484 1652551315.0 r 1283653858510598144 -1525579546338156553 1437952405283426310 1652561482.0 r 1283646922406760448 -1525907430894346241 1283646922406760448 1652639656.0 r 1437952405283426310 -1526047519716818947 1283646922406760448 1652673055.0 r 1390637197167038464 -1526048038640295937 1390637197167038464 1652673179.0 r 1283646922406760448 -1526048410259836928 1283646922406760448 1652673268.0 r 1390637197167038464 -1526060681216368642 1390637197167038464 1652676193.0 r 1283646922406760448 -1526580436339634176 1490867613915828224 1652800113.0 r 1283653858510598144 -1526729150375026689 1390209302120394754 1652835569.0 r 1283653858510598144 -1527161251507277824 1235180878449397764 1652938590.0 r 1437959162651156484 -1527178350191575041 1390209302120394754 1652942666.0 r 1283646922406760448 -1527427981164961792 1490867613915828224 1653002183.0 r 1283650008835743744 -1527513796989882385 1390637197167038464 1653022643.0 r 1283646922406760448 -1527540385735655425 1390209302120394754 1653028982.0 r 1283646922406760448 -1527973275514638337 1437952405283426310 1653132191.0 r 1235180878449397764 -1528530281128505344 1437961007029227520 1653264992.0 r 1283657064410017793 -1529247655884468224 1437952405283426310 1653436027.0 r 1328277233492844544 -1529252870922838017 1328277233492844544 1653437271.0 r 1437952405283426310 -1529686865460314112 1437952405283426310 1653540743.0 r 1486629076005634049 -1530454676621099008 1437952405283426310 1653723804.0 r 1486633489101307907 -1530680568911904769 1437952405283426310 1653777660.0 r 1283646922406760448 -1530681719342235649 1283646922406760448 1653777935.0 r 1437952405283426310 -1531002941129342986 1437952405283426310 1653854520.0 r 1234752200145899520 -1531375317923012608 1437959162651156484 1653943302.0 r 1328277233492844544 -1531479063788785664 1235180878449397764 1653968036.0 r 1390209302120394754 -1531834410634563586 1437959162651156484 1654052758.0 r 1283646922406760448 -1531836452925059073 1390637197167038464 1654053245.0 r 1409817941705515015 -1531841844908380160 1437959162651156484 1654054530.0 r 1409817941705515015 -1532194931120513024 1437952405283426310 1654138713.0 r 1235180878449397764 -1533715271793496064 1437959162651156484 1654501190.0 r 1283646922406760448 -1533828800907198465 1437952405283426310 1654528257.0 r 1234752200145899520 -1533876795413438465 1234752200145899520 1654539700.0 r 1437952405283426310 -1533992491116302336 1437959162651156484 1654567284.0 r 1409783149211443200 -1534807688302841856 1437959162651156484 1654761642.0 r 1409783149211443200 -1535648866858565633 1437952405283426310 1654962195.0 r 1234753886520393729 -1535653899251875842 1234753886520393729 1654963395.0 r 1437952405283426310 -1535707546840879105 1390209302120394754 1654976185.0 r 1283646922406760448 -1535707978296434688 1283646922406760448 1654976288.0 r 1390209302120394754 -1535818988739039232 1390637197167038464 1655002755.0 r 1283646922406760448 -1536737769216671746 1465851881180348425 1655221810.0 r 1283653858510598144 -1537161107009568768 1414849131655450626 1655322741.0 m 1283653858510598144 r 68042177 -1537568036655333382 1437961007029227520 1655419761.0 r 1283653858510598144 -1539506853797048320 1437952405283426310 1655882011.0 r 1234753886520393729 -1539531067459133440 1234753886520393729 1655887784.0 r 1437952405283426310 -1540385151888269312 1390209302120394754 1656091413.0 m 1283653858510598144 10228272 r 68042177 -1540422193527623680 1390637197167038464 1656100245.0 r 1283646922406760448 -1540456258708316161 1437961007029227520 1656108367.0 r 1283653858510598144 -1541285867184181249 1390209302120394754 1656306161.0 r 1283653858510598144 -1541338112311169025 1490867613915828224 1656318617.0 r 1283653858510598144 -1541470985253273602 1437952405283426310 1656350296.0 r 1234752200145899520 -1541478869810974721 1234752200145899520 1656352176.0 r 1437952405283426310 -1542324448132489216 1437959162651156484 1656553778.0 r 1409817941705515015 -1542562663955574785 1234753886520393729 1656610573.0 r 1390620618001838086 -1542563213983846400 1390620618001838086 1656610704.0 r 1234753886520393729 -1542652073715683332 1437959162651156484 1656631890.0 r 1409783149211443200 -1543899006509543428 1437952405283426310 1656929182.0 r 1486633489101307907 -1544844213220696066 1283646922406760448 1657154536.0 r 1390637197167038464 -1545136032944984075 1283646922406760448 1657224112.0 r 1390637197167038464 -1545177930216706051 1390637197167038464 1657234101.0 r 1283646922406760448 -1545856111147876354 1437952405283426310 1657395792.0 r 1328277233492844544 -1546025534253154305 1390209302120394754 1657436185.0 r 1283646922406760448 -1547059850378723328 1390637197167038464 1657682785.0 r 1283646922406760448 -1547533660424966144 1437961007029227520 1657795751.0 r 1328277233492844544 -1547535376037908480 1328277233492844544 1657796160.0 r 1437961007029227520 -1547543631585689600 1437961007029227520 1657798128.0 r 1328277233492844544 -1547822007240101888 1437961007029227520 1657864498.0 r 1328277233492844544 -1547822611572305922 1328277233492844544 1657864642.0 r 1437961007029227520 -1547823238373199874 1437961007029227520 1657864791.0 r 1328277233492844544 -1547827534141669377 1437952405283426310 1657865816.0 r 1235180878449397764 -1549005044502118401 1465851243167895554 1658146556.0 r 1536577295632441344 -1549015819463827456 1536577295632441344 1658149125.0 r 1465851243167895554 -1549053693202501634 1465851881180348425 1658158155.0 r 1536577295632441344 -1549053939198611461 1465851881180348425 1658158213.0 m 1536577295632441344 r 1314212540180652033 -1549054754047016960 1536577295632441344 1658158408.0 r 1465851881180348425 -1549614780461322241 1437952405283426310 1658291928.0 m 4653721512 r 1283653858510598144 -1549616647299690496 1283653858510598144 1658292373.0 r 1437952405283426310 -1550099465704509445 1465851881180348425 1658407486.0 r 1283653858510598144 -1550218515658964995 1437952405283426310 1658435870.0 r 1486636197908602880 -1550247118249525248 1465851243167895554 1658442689.0 r 1283653858510598144 -1550275830915338240 1283653858510598144 1658449535.0 r 1465851243167895554 -1550277217862569984 1283653858510598144 1658449866.0 r 1465851881180348425 -1550291993821061125 1437952405283426310 1658453389.0 r 1235180878449397764 -1550297659524595714 1545354510515654656 1658454739.0 r 1536575088996524032 -1550322860962758656 1546328834559340544 1658460748.0 r 1536575088996524032 -1550331964674035713 1545354510515654656 1658462918.0 r 1536579341332516864 -1550332386931486725 1545354510515654656 1658463019.0 r 1536577295632441344 -1550332591030489088 1545354510515654656 1658463068.0 r 1536576325296996352 -1550342421116256256 1536577295632441344 1658465411.0 r 1545354510515654656 -1550343354067460097 1536576325296996352 1658465634.0 r 1545354510515654656 -1550350835334254592 1545354510515654656 1658467417.0 r 1536576325296996352 -1550351390739861507 1545354510515654656 1658467550.0 r 1536577295632441344 -1550397257874165760 1545351225293426688 1658478485.0 r 1536575088996524032 -1550399313888432128 1545351225293426688 1658478976.0 r 1536576325296996352 -1550562124765704192 1545354510515654656 1658517793.0 r 1536575088996524032 -1550646190386974720 1536576325296996352 1658537836.0 r 1545354510515654656 -1550647889038229504 1545354510515654656 1658538241.0 r 1536576325296996352 -1550648071200985089 1545354510515654656 1658538284.0 r 1536576325296996352 -1550662057967890432 1546328834559340544 1658541619.0 r 1536575088996524032 -1550664135393681409 1545352592884084736 1658542114.0 r 1536575088996524032 -1550664352675418112 1545354510515654656 1658542166.0 r 1536575088996524032 -1550664735179300872 1545562635650957312 1658542257.0 r 1536575088996524032 -1550677352819990528 1545354510515654656 1658545265.0 r 1536576325296996352 -1550678489585442816 1545352592884084736 1658545536.0 r 1536576325296996352 -1550679354480410624 1545562635650957312 1658545742.0 r 1536576325296996352 -1550734447439675396 1536576325296996352 1658558878.0 r 1545354510515654656 -1550811800551034880 1536576325296996352 1658577320.0 r 1545352592884084736 -1550837092409692160 1545354510515654656 1658583350.0 r 1536577295632441344 -1550838299337818119 1536577295632441344 1658583638.0 r 1545354510515654656 -1551004664027500545 1536576325296996352 1658623302.0 r 1545354510515654656 -1551004708755496960 1545354510515654656 1658623313.0 r 1536579341332516864 -1551015482555113475 1545354510515654656 1658625882.0 r 1536576325296996352 -1551046378453102592 1165866977715347456 1658633248.0 r 1536576325296996352 -1551073233578954753 1545562635650957312 1658639651.0 r 1536579341332516864 -1551073272258670592 1545354510515654656 1658639660.0 r 1536579341332516864 -1551073404052131840 1545354510515654656 1658639691.0 r 1536577295632441344 -1551073609581486082 1545562635650957312 1658639740.0 r 1536577295632441344 -1551086183488565248 1536577295632441344 1658642738.0 r 1545354510515654656 -1551086503735881731 1545354510515654656 1658642814.0 r 1536577295632441344 -1551315694800621568 1536576325296996352 1658697458.0 r 1545354510515654656 -1551316049940779008 1545354510515654656 1658697543.0 r 1536576325296996352 -1551317426632998912 1536576325296996352 1658697871.0 r 1545562635650957312 -1551324952284090374 1490867613915828224 1658699665.0 r 1536576325296996352 -1551342848821395457 1536575088996524032 1658703932.0 r 1545352592884084736 -1551343156272058368 1536575088996524032 1658704005.0 r 1545562635650957312 -1551343436451684352 1536575088996524032 1658704072.0 r 1545354510515654656 -1551343743495806976 1536575088996524032 1658704145.0 r 1545552756773208066 -1551344100699475968 1536575088996524032 1658704230.0 r 1545351225293426688 -1551344214591492096 1545354510515654656 1658704257.0 r 1536575088996524032 -1551344386755112960 1536576325296996352 1658704299.0 r 1545562635650957312 -1551344395710263296 1536577295632441344 1658704301.0 r 1545354510515654656 -1551344539624886272 1545354510515654656 1658704335.0 r 1536577295632441344 -1551344551696257031 1536575088996524032 1658704338.0 r 1546328834559340544 -1551405816250769408 1536577295632441344 1658718944.0 r 1545354510515654656 -1551424524268158978 1536576325296996352 1658723405.0 r 1545354510515654656 -1551436413165391872 1536576325296996352 1658726239.0 r 1490867613915828224 -1551456589499863040 1536576325296996352 1658731050.0 r 1545354510515654656 -1551457846948032513 1545354510515654656 1658731350.0 r 1536576325296996352 -1551461115808272384 1536576325296996352 1658732129.0 r 1545354510515654656 -1551461250076250113 1545354510515654656 1658732161.0 r 1536576325296996352 -1551466570290868224 1536579341332516864 1658733429.0 r 1545562635650957312 -1551882014923673601 1465851881180348425 1658832479.0 r 1536576325296996352 -1551884728613015553 1536576325296996352 1658833126.0 r 1465851881180348425 -1552586177819152386 1437961007029227520 1659000365.0 r 1536576325296996352 -1552589250670915584 1536576325296996352 1659001097.0 r 1437961007029227520 -1552825070627631105 1413318241804439552 1659057321.0 r 1536576325296996352 -1552825746120273920 1536576325296996352 1659057482.0 r 1413318241804439552 -1552828067214606336 1413318241804439552 1659058036.0 r 1536576325296996352 -1552888849734451201 1491195742123397124 1659072527.0 m 1536577295632441344 r 1536576325296996352 -1552919300641419264 1536576325296996352 1659079787.0 m 1536577295632441344 r 1491195742123397124 -1552939794040123392 1536576325296996352 1659084673.0 m 2965034410 r 1493392149664219138 -1552948980996194306 1536576325296996352 1659086864.0 r 1465851881180348425 -1552950846631059457 1465851881180348425 1659087308.0 r 1536576325296996352 -1552951158201020416 1536577295632441344 1659087383.0 r 1465851881180348425 -1552951496031014913 1465851881180348425 1659087463.0 m 1536576325296996352 r 1462429128951578631 -1552953954471927808 1465851881180348425 1659088049.0 r 1536577295632441344 -1552956457011712000 1165866977715347456 1659088646.0 r 1536575088996524032 -1553014919918137345 1413339084076978179 1659102585.0 r 1328277233492844544 -1553027976484343810 1493392149664219138 1659105698.0 m 2965034410 r 1536576325296996352 -1553034936394715136 1328277233492844544 1659107357.0 r 1413339084076978179 -1553170656363270144 1545562635650957312 1659139715.0 r 1536579341332516864 -1553370407834595328 1536576325296996352 1659187340.0 r 1465851881180348425 -1553542812737703936 1437952405283426310 1659228444.0 r 1409819816194576394 -1554042975025598465 1283646922406760448 1659347692.0 r 1390209302120394754 -1554047583064145920 1390209302120394754 1659348791.0 r 1283646922406760448 -1554155752406671360 1545354510515654656 1659374580.0 r 1536576325296996352 -1554341585906802688 1437952405283426310 1659418887.0 r 1234753886520393729 -1554389572741541888 1545354510515654656 1659430327.0 r 1536575088996524032 -1554454504250249218 1545552756773208066 1659445808.0 r 1536577295632441344 -1554455781818667010 1536577295632441344 1659446113.0 r 1545552756773208066 -1554465118511144961 1545552756773208066 1659448339.0 r 1536577295632441344 -1554689261747707906 1390637197167038464 1659501779.0 r 1409784760805650436 -1554691845828931584 1409784760805650436 1659502395.0 r 1390637197167038464 -1554698262665379845 1437952405283426310 1659503925.0 r 1234752200145899520 -1554715080436203522 1545552756773208066 1659507935.0 r 1536579341332516864 -1554720119766810624 1546328834559340544 1659509136.0 r 1536579341332516864 -1554768787399970818 1437961007029227520 1659520739.0 r 1283653858510598144 -1554774647836528640 1283653858510598144 1659522137.0 r 1437961007029227520 -1554775506662334466 1437961007029227520 1659522341.0 r 1283653858510598144 -1554912235067162624 1536575088996524032 1659554940.0 r 1545552756773208066 -1554913328127827969 1536576325296996352 1659555200.0 r 1545552756773208066 -1554913467622105088 1545552756773208066 1659555234.0 r 1536576325296996352 -1554913619443326976 1545552756773208066 1659555270.0 r 1536575088996524032 -1554920867943120898 1437952405283426310 1659556998.0 r 1328275136575799297 -1554921627758075909 1328275136575799297 1659557179.0 r 1437952405283426310 -1554972517894426630 1545552756773208066 1659569312.0 r 1536575088996524032 -1554981715357097984 1536575088996524032 1659571505.0 r 1545552756773208066 -1555017497228738560 1390637197167038464 1659580036.0 r 1328277233492844544 -1555023013858254851 1328277233492844544 1659581352.0 r 1390637197167038464 -1555314577042092033 1536576325296996352 1659650866.0 r 1545552756773208066 -1555315941667373064 1545552756773208066 1659651191.0 r 1536576325296996352 -1555328937466638338 1536576325296996352 1659654289.0 r 1545562635650957312 -1555357825366478849 1390209302120394754 1659661177.0 r 1283646922406760448 -1555516860044611584 1490867613915828224 1659699094.0 r 1328277233492844544 -1555721378506354688 1546328834559340544 1659747855.0 r 1536575088996524032 -1555726002231427072 1465851881180348425 1659748957.0 r 1536575088996524032 -1555729669051269120 1536575088996524032 1659749831.0 r 1465851881180348425 -1555739217749319680 1465851881180348425 1659752108.0 r 1536575088996524032 -1555740727103070209 1536576325296996352 1659752468.0 r 1465851881180348425 -1555847956468797440 1465851881180348425 1659778033.0 r 1536576325296996352 -1555859373683245056 1546328834559340544 1659780755.0 r 1536576325296996352 -1555870249358016515 1328277233492844544 1659783348.0 r 1437961007029227520 -1555873683826249728 1437961007029227520 1659784167.0 r 1328277233492844544 -1555975215838003200 1536575088996524032 1659808374.0 r 1545552756773208066 -1555976078585389057 1545552756773208066 1659808580.0 r 1536575088996524032 -1555978341345263616 1536575088996524032 1659809119.0 r 1545552756773208066 -1555978589052588032 1545552756773208066 1659809178.0 r 1536575088996524032 -1556237656480423937 1546328834559340544 1659870945.0 r 1536577295632441344 -1556245110027087874 1546328834559340544 1659872722.0 r 1283653858510598144 -1556444073183035392 1536575088996524032 1659920159.0 r 1465851881180348425 -1556532056397586432 1545552756773208066 1659941135.0 r 1536575088996524032 -1556542676216041472 1545552756773208066 1659943667.0 r 1536576325296996352 -1556592626664501248 1536576325296996352 1659955576.0 r 1545552756773208066 -1556681014076145664 1546328834559340544 1659976650.0 r 1283653858510598144 -1556770622759669760 1546328834559340544 1659998014.0 r 1536575088996524032 -1556872820566671360 1536575088996524032 1660022380.0 r 1546328834559340544 -1557015648529776640 1465850835951357955 1660056433.0 r 1328277233492844544 -1557015974016319489 1328277233492844544 1660056510.0 r 1465850835951357955 -1557071625329205248 1491195742123397124 1660069779.0 m 1125905906879717376 r 1536576325296996352 -1557071857236320256 1536576325296996352 1660069834.0 m 1125905906879717376 r 1491195742123397124 -1557072641554513921 1491195742123397124 1660070021.0 m 1125905906879717376 r 1536576325296996352 -1557072692087603205 1491195742123397124 1660070033.0 m 1536576325296996352 r 1125905906879717376 -1557389861249761280 1465851243167895554 1660145652.0 m 1414849131655450626 r 1328277233492844544 -1557390595773018112 1328277233492844544 1660145827.0 m 1414849131655450626 r 1465851243167895554 -1557508080883081216 1437959162651156484 1660173838.0 r 1536579341332516864 -1557522514099998723 1546328834559340544 1660177279.0 r 1283653858510598144 -1557613391421837312 1437959162651156484 1660198946.0 r 1536576325296996352 -1557659226322833408 1536576325296996352 1660209874.0 r 1437959162651156484 -1557659649771393025 1536576325296996352 1660209975.0 r 1545354510515654656 -1557660708187381761 1545354510515654656 1660210227.0 r 1536576325296996352 -1557741534128533505 1546328834559340544 1660229497.0 r 1328277233492844544 -1557752283504513024 1465851881180348425 1660232060.0 r 1536576325296996352 -1557755495074643968 1437959162651156484 1660232826.0 r 1536576325296996352 -1557759489033396224 1536576325296996352 1660233778.0 r 1465851881180348425 -1557761527092072448 1536575088996524032 1660234264.0 m 1536576325296996352 r 1465851881180348425 -1557881987091664898 1546328834559340544 1660262984.0 r 1409783149211443200 -1558231465179205637 1536575088996524032 1660346306.0 r 1437952405283426310 -1558256611763912707 1437952405283426310 1660352301.0 r 1536575088996524032 -1558506058028617729 1437952405283426310 1660411774.0 r 1486633489101307907 -1558506761727856640 1486633489101307907 1660411942.0 r 1437952405283426310 -1558506957035675649 1437952405283426310 1660411988.0 r 1486633489101307907 -1558509965421297664 1437952405283426310 1660412706.0 r 1328277750000492545 -1558515639077449728 1486633489101307907 1660414058.0 r 1437952405283426310 -1558545460289216515 1437952405283426310 1660421168.0 r 1328275136575799297 -1558547119438700544 1328275136575799297 1660421564.0 r 1437952405283426310 -1558638172703186944 1437959162651156484 1660443273.0 r 1536577295632441344 -1558641960499675136 1536577295632441344 1660444176.0 r 1437959162651156484 -1558768134089486338 1328277750000492545 1660474258.0 r 1437952405283426310 -1558840388135550976 1414849131655450626 1660491485.0 r 1283653858510598144 -1559211137614622722 1536577295632441344 1660579878.0 r 1545354510515654656 -1559211456444243968 1545354510515654656 1660579954.0 r 1536577295632441344 -1559463719033180161 1328277233492844544 1660640098.0 r 1413318241804439552 -1559619634814365696 1545354510515654656 1660677271.0 r 1536576325296996352 -1559669937269985280 1536576325296996352 1660689264.0 r 1545354510515654656 -1559676089953304577 1545562635650957312 1660690731.0 r 1536576325296996352 -1559998316871618561 1545562635650957312 1660767556.0 r 1536576325296996352 -1560039854242574336 1536576325296996352 1660777460.0 r 1545562635650957312 -1560066934686695424 1437959162651156484 1660783916.0 r 1536576325296996352 -1560074291181301760 1536576325296996352 1660785670.0 r 1437959162651156484 -1560076141909532672 1437959162651156484 1660786111.0 m 1536576325296996352 r 2895303462 -1560328225645072387 1465851188562345985 1660846213.0 r 1283653858510598144 -1560332770760605697 1437952405283426310 1660847296.0 r 1536575088996524032 -1560337010933567490 1536575088996524032 1660848307.0 r 1437952405283426310 -1560337440765771780 1437952405283426310 1660848410.0 r 1536575088996524032 -1560439973555445761 1545552756773208066 1660872855.0 r 1536576325296996352 -1560613113921257472 1437952405283426310 1660914135.0 r 1234752200145899520 -1560796173266522113 1465851188562345985 1660957780.0 r 1536576325296996352 -1560798630889811968 1536576325296996352 1660958366.0 r 1465851188562345985 -1560803219260981249 1465851188562345985 1660959460.0 r 1536576325296996352 -1560864577516101634 1390209302120394754 1660974089.0 r 1409817941705515015 -1561100935484432385 1409817941705515015 1661030441.0 r 1390209302120394754 -1561127885028704258 1491195742123397124 1661036866.0 r 1536575088996524032 -1561265209918078977 1536575088996524032 1661069607.0 r 1490867613915828224 -1561477160669327361 1536576325296996352 1661120140.0 m 1545352592884084736 r 1545562635650957312 -1561484885990051840 1490867613915828224 1661121982.0 r 1536575088996524032 -1561486362527862791 1545562635650957312 1661122334.0 m 1545352592884084736 r 1536576325296996352 -1561613889279823879 1437952405283426310 1661152739.0 r 1328275136575799297 -1561614828250075137 1328275136575799297 1661152963.0 r 1437952405283426310 -1561770296012840960 1328277233492844544 1661190029.0 r 1390637197167038464 -1561828116523270145 1536575088996524032 1661203814.0 r 1437952405283426310 -1561828526101258241 1437952405283426310 1661203912.0 r 1536575088996524032 -1561835979413110784 1437952405283426310 1661205689.0 r 1536576325296996352 -1561838334560706561 1536576325296996352 1661206251.0 r 1437952405283426310 -1561862817145393152 1437952405283426310 1661212088.0 r 1536576325296996352 -1561864873587478529 1490867613915828224 1661212578.0 r 1536576325296996352 -1561923387710005248 1536576325296996352 1661226529.0 r 1490867613915828224 -1561956282986180608 1546328834559340544 1661234372.0 r 1536575088996524032 -1562208359159238656 1390620618001838086 1661294471.0 r 1283646922406760448 -1562209945390571520 1283646922406760448 1661294850.0 r 1390620618001838086 -1562264476036268033 1546328834559340544 1661307851.0 r 1283653858510598144 -1562265066820743170 1546328834559340544 1661307992.0 r 1283657064410017793 -1562332730612129794 1546328834559340544 1661324124.0 r 1536577295632441344 -1562333578876588034 1536577295632441344 1661324326.0 r 1546328834559340544 -1562334843123699712 1546328834559340544 1661324628.0 r 1536577295632441344 -1562544450059964417 1437959162651156484 1661374602.0 r 1536576325296996352 -1562592277343588352 1536576325296996352 1661386005.0 r 1437959162651156484 -1562600887084003328 1437963160544284675 1661388057.0 r 1409817941705515015 -1562632581451255810 1490867613915828224 1661395614.0 r 1328277233492844544 -1562633559092842498 1328277233492844544 1661395847.0 r 1490867613915828224 -1562850562873237505 1437952405283426310 1661447585.0 r 1235180878449397764 -1562853031086985216 1235180878449397764 1661448173.0 r 1437952405283426310 -1562933650617942016 1546328834559340544 1661467394.0 r 1536577295632441344 -1562933821254864896 1437952405283426310 1661467435.0 r 1235180878449397764 -1563423011654758400 1490867613915828224 1661584067.0 r 1409783149211443200 -1563423479063404548 1490867613915828224 1661584179.0 r 1328277233492844544 -1563439736932675585 1328277233492844544 1661588055.0 r 1490867613915828224 -1563591333050466307 1283646922406760448 1661624198.0 r 1413326894435602434 -1563591528336879617 1283646922406760448 1661624245.0 r 1413339084076978179 -1563597365176836096 1413339084076978179 1661625636.0 r 1283646922406760448 -1563601129099100161 1283646922406760448 1661626534.0 r 1413339084076978179 -1563719426473615361 1413326894435602434 1661654738.0 r 1283646922406760448 -1563828400699363328 1413339084076978179 1661680719.0 r 1283646922406760448 -1564019189777571840 1490867613915828224 1661726207.0 r 1536575088996524032 -1564066469838725120 1545552756773208066 1661737480.0 r 1536575088996524032 -1564244223708504065 1328277233492844544 1661779859.0 r 1490867613915828224 -1564379564431663105 1536576325296996352 1661812127.0 r 1545552756773208066 -1564379988442447872 1545552756773208066 1661812228.0 r 1536576325296996352 -1564380278600056837 1536576325296996352 1661812297.0 r 1545552756773208066 -1564380603184816128 1545552756773208066 1661812375.0 r 1536576325296996352 -1564434962270392320 1545354510515654656 1661825335.0 r 1536576325296996352 -1565023679016222722 1490867613915828224 1661965696.0 r 1536576325296996352 -1565025366401757184 1546328834559340544 1661966098.0 r 1536576325296996352 -1565113756476006401 1536576325296996352 1661987172.0 r 1490867613915828224 -1565114081341624320 1536576325296996352 1661987250.0 r 1546328834559340544 -1565137357245976579 1490867613915828224 1661992799.0 r 1536576325296996352 -1565138521551097856 1536576325296996352 1661993077.0 r 1490867613915828224 -1565152149855784960 1490867613915828224 1661996326.0 r 1536576325296996352 -1565377543229906944 1545552756773208066 1662050064.0 r 1536575088996524032 -1565413410116091904 1546328834559340544 1662058615.0 r 1536579341332516864 -1565543532353359873 1328277233492844544 1662089639.0 m 1491195742123397124 r 2862272008 -1565557271299596289 1491195742123397124 1662092914.0 m 1328277233492844544 r 2862272008 -1565579604248539139 1409784760805650436 1662098239.0 r 1390209302120394754 -1565590181402615809 1390209302120394754 1662100761.0 r 1409784760805650436 -1565703698017402888 1437952405283426310 1662127825.0 r 1234752200145899520 -1565721005666213888 1546328834559340544 1662131952.0 r 1283646922406760448 -1565721555006812160 1546328834559340544 1662132083.0 r 1536576325296996352 -1565734786110574592 1545552756773208066 1662135237.0 r 1536576325296996352 -1565767909846224897 1545552756773208066 1662143134.0 r 1536575088996524032 -1565870141539094529 1536576325296996352 1662167508.0 r 1437959162651156484 -1565870528426131456 1437959162651156484 1662167601.0 r 1536576325296996352 -1565870884123860992 1536576325296996352 1662167685.0 r 1437959162651156484 -1565871907978739713 1437959162651156484 1662167930.0 r 1536576325296996352 -1565871990526955521 1437959162651156484 1662167949.0 r 1536576325296996352 -1565874495977848833 1546328834559340544 1662168547.0 r 1536575088996524032 -1565875119502700544 1536576325296996352 1662168695.0 r 1437959162651156484 -1566111813208801287 1546328834559340544 1662225127.0 r 1283656034305769472 -1566197184453910528 1283646922406760448 1662245481.0 r 1413326894435602434 -1566197326632345606 1283646922406760448 1662245515.0 r 1413326894435602434 -1566206041041342464 1413326894435602434 1662247593.0 r 1283646922406760448 -1566299781193940996 1491195742123397124 1662269942.0 r 1536575088996524032 -1566299831731200002 1491195742123397124 1662269954.0 r 1536576325296996352 -1566300511552290816 1536575088996524032 1662270117.0 r 1491195742123397124 -1566309160089751552 1491195742123397124 1662272179.0 r 1536577295632441344 -1566309185288953856 1536576325296996352 1662272185.0 r 1491195742123397124 -1566317277733175296 1536575088996524032 1662274114.0 r 1437952405283426310 -1566331132794355712 1491195742123397124 1662277417.0 r 1536579341332516864 -1566346783533412352 1536577295632441344 1662281149.0 r 1491195742123397124 -1566429593849970689 1491195742123397124 1662300892.0 r 1536577295632441344 -1566459522708914176 1491195742123397124 1662308028.0 r 1536577295632441344 -1566474269843922946 1536575088996524032 1662311544.0 r 1490867613915828224 -1566474518440382464 1490867613915828224 1662311603.0 r 1536575088996524032 -1566487328427855872 1283646922406760448 1662314657.0 r 1413326894435602434 -1566511654703095808 1545552756773208066 1662320457.0 r 1536577295632441344 -1566554690766110725 1413326894435602434 1662330718.0 r 1283646922406760448 -1566595377502908416 1536577295632441344 1662340418.0 r 1545552756773208066 -1566603163830226946 1536577295632441344 1662342274.0 r 1545552756773208066 -1566607875761389571 1437959162651156484 1662343398.0 r 1536576325296996352 -1566750155671764993 1546328834559340544 1662377320.0 r 1328277233492844544 -1566750613609652224 1328277233492844544 1662377429.0 r 1546328834559340544 -1566755355073613825 1546328834559340544 1662378560.0 r 1328277233492844544 -1566800246595719170 1328277233492844544 1662389263.0 r 1546328834559340544 -1566804138079719431 1546328834559340544 1662390190.0 r 1328277233492844544 -1566804605110849537 1328277233492844544 1662390302.0 r 1546328834559340544 -1566806607358722048 1546328834559340544 1662390779.0 r 1328277233492844544 -1566806801995116544 1328277233492844544 1662390826.0 r 1546328834559340544 -1566809501160062976 1546328834559340544 1662391469.0 r 1328277233492844544 -1566809944783212544 1546328834559340544 1662391575.0 r 1536576325296996352 -1567514035196940289 1546328834559340544 1662559443.0 r 1283646922406760448 -1567736479052611586 1546328834559340544 1662612478.0 r 1536577295632441344 -1567837054301601792 1437959162651156484 1662636457.0 r 1536576325296996352 -1567930208379871234 1490867613915828224 1662658667.0 r 1328277233492844544 -1567944274066702342 1328277233492844544 1662662020.0 r 1490867613915828224 -1568024710624968705 1490867613915828224 1662681198.0 r 1283646922406760448 -1568649267870601217 1536577295632441344 1662830104.0 r 1546328834559340544 -1568657774375993346 1490867613915828224 1662832132.0 r 1328277233492844544 -1568657897713991683 1546328834559340544 1662832161.0 r 1536577295632441344 -1568823166667030528 1545552756773208066 1662871564.0 r 1536576325296996352 -1568825289702281216 1536576325296996352 1662872071.0 r 1545552756773208066 -1568890499658022913 1328277233492844544 1662887618.0 r 1437952405283426310 -1568923179544100866 1546328834559340544 1662895409.0 r 1328277233492844544 -1569009589659779073 1546328834559340544 1662916011.0 r 1409783149211443200 -1569099902482882568 1390209302120394754 1662937543.0 m 449769183 r 1283646922406760448 -1569113833234391040 1283646922406760448 1662940865.0 r 1390209302120394754 -1569540622738157576 1546328834559340544 1663042619.0 r 1283657064410017793 -1569541148527718400 1546328834559340544 1663042745.0 r 1536577295632441344 -1569797962007740417 1536576325296996352 1663103974.0 r 1545562635650957312 -1569881107100831744 1536575088996524032 1663123797.0 r 1490867613915828224 -1569881410663596036 1490867613915828224 1663123869.0 r 1536575088996524032 -1569952707603234819 1328277233492844544 1663140868.0 m 1490867613915828224 -1569952988373946369 1490867613915828224 1663140935.0 r 1328277233492844544 -1570167249423695872 1437961007029227520 1663192019.0 r 1283653858510598144 -1570177164376526848 1465851881180348425 1663194383.0 r 1283653858510598144 -1570290396617650176 1390620618001838086 1663221379.0 r 1409817941705515015 -1570392377990475776 1546328834559340544 1663245694.0 r 1536577295632441344 -1570669269624705025 1536577295632441344 1663311710.0 r 1546328834559340544 -1570738744856309761 1546328834559340544 1663328274.0 r 1536577295632441344 -1570779682035101698 1437952405283426310 1663338034.0 r 1234753886520393729 -1570779797432995841 1437952405283426310 1663338062.0 r 1234753886520393729 -1571059833285279744 1536576325296996352 1663404827.0 r 1545562635650957312 -1571397151397416961 1546328834559340544 1663485250.0 r 1328277233492844544 -1571398316222722049 1328277233492844544 1663485528.0 r 1546328834559340544 -1571516498153451521 1390209302120394754 1663513705.0 r 1283646922406760448 -1571551194715234306 1390620618001838086 1663521977.0 r 1283646922406760448 -1571554028806459393 1283646922406760448 1663522653.0 r 1390620618001838086 -1571589817997340677 1437952405283426310 1663531185.0 r 1234752200145899520 -1571842179483709440 1234752200145899520 1663591353.0 r 1437952405283426310 -1572004103437385729 1283646922406760448 1663629959.0 m 10228272 r 1437961007029227520 -1572007571875319808 1437961007029227520 1663630786.0 m 10228272 r 1283646922406760448 -1572007927354892288 1283646922406760448 1663630871.0 m 10228272 r 1437961007029227520 -1572012241477857281 1437961007029227520 1663631899.0 m 10228272 r 1283646922406760448 -1572014393671442437 1390637197167038464 1663632412.0 r 1283646922406760448 -1572016325450661888 1283646922406760448 1663632873.0 r 1437961007029227520 -1572016412356448257 1437961007029227520 1663632894.0 r 1283646922406760448 -1572016577738080256 1283646922406760448 1663632933.0 r 1390637197167038464 -1572016752145358848 1283646922406760448 1663632975.0 r 1437961007029227520 -1572017123643252742 1437961007029227520 1663633063.0 r 1283646922406760448 -1572043236038025218 1546328834559340544 1663639289.0 r 1409817096523968513 -1572096788311457793 1465851188562345985 1663652057.0 r 1283653858510598144 -1572285407496404992 1283653858510598144 1663697027.0 r 1465851188562345985 -1572292092017389568 1390637197167038464 1663698621.0 r 1283646922406760448 -1572306804583051264 1465850835951357955 1663702128.0 r 1328277233492844544 -1572311441876856832 1328277233492844544 1663703234.0 r 1465850835951357955 -1572458241422163970 1546328834559340544 1663738234.0 r 1409783149211443200 -1572492692327927810 1536576325296996352 1663746448.0 r 1437959162651156484 -1572494159436546048 1437959162651156484 1663746797.0 r 1536576325296996352 -1572600858554298371 1546328834559340544 1663772236.0 r 1328277233492844544 -1572601099424768000 1328277233492844544 1663772294.0 r 1546328834559340544 -1572601192525746176 1328277233492844544 1663772316.0 r 1546328834559340544 -1572602296701423617 1546328834559340544 1663772579.0 r 1328277233492844544 -1572631678836740096 1465851881180348425 1663779584.0 r 1536576325296996352 -1572828124139888642 1323147856828510208 1663826421.0 r 1536576325296996352 -1572829818827448321 1536576325296996352 1663826825.0 r 1465851881180348425 -1572973053914787844 1546328834559340544 1663860975.0 r 1283653858510598144 -1572994930070687750 1546328834559340544 1663866190.0 r 1283646922406760448 -1573009012345524226 1390620618001838086 1663869548.0 r 1328277233492844544 -1573010080379080705 1328277233492844544 1663869802.0 r 1390620618001838086 -1573019097142624262 1437959162651156484 1663871952.0 m 1283653858510598144 r 1546328834559340544 -1573038227602259968 1465851881180348425 1663876513.0 r 1536576325296996352 -1573204474793172992 1546328834559340544 1663916150.0 r 1409817096523968513 -1573264760845524997 1437959162651156484 1663930523.0 r 1536576325296996352 -1573294842935119873 1536576325296996352 1663937695.0 r 1465851188562345985 -1573296851226603521 1465851188562345985 1663938174.0 r 1536576325296996352 -1573298287561809922 1536576325296996352 1663938516.0 r 1437959162651156484 -1573363265589358592 1546328834559340544 1663954008.0 m 1328277233492844544 r 1283646922406760448 -1573435733884702721 1536576325296996352 1663971286.0 r 1465851881180348425 -1573440061747634176 1465851881180348425 1663972318.0 r 1536576325296996352 -1573564004747952128 1536575088996524032 1664001868.0 m 1490867613915828224 -1573564005397970945 1490867613915828224 1664001868.0 m 1536575088996524032 -1573564304313708545 1536577295632441344 1664001940.0 m 1490867613915828224 r 1536575088996524032 -1573564439009296384 1490867613915828224 1664001972.0 m 1536575088996524032 r 1536577295632441344 -1573564462128414720 1536575088996524032 1664001977.0 m 1490867613915828224 r 1536577295632441344 -1573565538214748160 1390209302120394754 1664002234.0 m 1536575088996524032 r 1490867613915828224 -1573670852024991744 1437959162651156484 1664027343.0 r 1536577295632441344 -1573689773822279680 1283646922406760448 1664031854.0 r 1437961007029227520 -1573689914717310986 1283646922406760448 1664031888.0 m 1437961007029227520 r 1437959162651156484 -1573695476712579073 1437959162651156484 1664033214.0 m 1437961007029227520 r 1283646922406760448 -1573725924557832193 1437961007029227520 1664040473.0 m 1283646922406760448 r 1437959162651156484 -1573726074470547458 1437961007029227520 1664040509.0 r 1283646922406760448 -1573727194379059202 1283646922406760448 1664040776.0 r 1437961007029227520 -1573732619556118529 1437961007029227520 1664042069.0 r 1283646922406760448 -1573740264186855426 1437959162651156484 1664043892.0 m 1283646922406760448 r 1437961007029227520 -1573744106383503360 1492604168145539072 1664044808.0 r 1283653858510598144 -1573797167826968577 1437959162651156484 1664057459.0 r 1536579341332516864 -1573881132671721475 1437959162651156484 1664077477.0 r 1283646922406760448 -1573901085391175683 1437959162651156484 1664082235.0 r 1283646922406760448 -1573982252131766272 1234753886520393729 1664101586.0 r 1390209302120394754 -1573998209139200001 1390209302120394754 1664105391.0 r 1234753886520393729 -1573999966926032896 1234753886520393729 1664105810.0 m 1390209302120394754 r 980488216523415552 -1574045946924892162 1437959162651156484 1664116772.0 r 1409783149211443200 -1574137781403627520 1490867613915828224 1664138667.0 r 1536575088996524032 -1574141511230439426 1536575088996524032 1664139557.0 r 1490867613915828224 -1574217702180528128 1546328834559340544 1664157722.0 r 1283646922406760448 -1574287657500381185 1437959162651156484 1664174401.0 r 1283646922406760448 -1574287762106339330 1437959162651156484 1664174425.0 r 1536579341332516864 -1574288691740172289 1437959162651156484 1664174647.0 m 1283646922406760448 r 451900970 -1574350291595657218 1437961007029227520 1664189334.0 r 1536576325296996352 -1574353713027682304 1536576325296996352 1664190149.0 r 1437961007029227520 -1574581679565455360 1390209302120394754 1664244501.0 r 1283653858510598144 -1574607516931133443 1437952405283426310 1664250661.0 r 1234752200145899520 -1574653852082577408 1536575088996524032 1664261708.0 r 1437952405283426310 -1574906115938295809 1437952405283426310 1664321853.0 r 1234753886520393729 -1574971612809801728 1413318241804439552 1664337468.0 r 1486633489101307907 -1574982174024925185 1545354510515654656 1664339986.0 r 1536576325296996352 -1574990002743672832 1486633489101307907 1664341853.0 r 1413318241804439552 From 404cb8a7ab3f260e54db8afdf3421d0dd5989ee6 Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Fri, 18 Aug 2023 02:06:13 -0700 Subject: [PATCH 17/18] Update README.md --- README.md | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c036f0a..ba3dbc3 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,24 @@ Twitter bot that tracks cross-company interactions between the non-JP branches o **This project was created to run [this account](https://twitter.com/NijiHolo_EN_ID).** +## Running +Install dependencies. +``` +pip install -r requirements.txt +``` +Setup the `.env` in the project root. Refer to the `.env` section for variables. + +Run the program from project root (not in `src`). Refer to the following section for options. +``` +python src/main.py +``` + +## Modes & Options +The bot may run in these modes: +* Pass no argument to run in listen mode, which scrapes all accounts in the *list* folder at an interval. + * Pass `--straight-to-queue` to process the locally-stored queue first before attempting to scrape. +* Command-line (`cmd`): an interactive mode for manual control and debugging (drops into Python interpretor) + ## `.env` These need to be defined in a `.env` file at the project root (outside of `src`): @@ -21,7 +39,7 @@ scraper_password0= scraper_username1= scraper_password1= ``` -The first account (`scraper_username0` and `scraper_password0`) will be used to attempt scraping private accounts. Make sure this account follows any private accounts that you want to scrape! +The first account (`scraper_username0` and `scraper_password0`) **MUST be defined (`scraper_username` and `scraper_password` without number will not work!)** and will be used to attempt scraping private accounts. Make sure this account follows any private accounts that you want to scrape! ### Twitter API Stuff The following keys/tokens are used for the official API via `tweepy`. We mainly use these to just post tweets. ``` @@ -35,11 +53,21 @@ This is the authentication token obtained from a browser when signed in on the T ``` web_auth_token= ``` - -## Running modes -The bot may run in these modes: -* Pass no argument to run in listen mode, which scrapes all accounts in the *list* folder at an interval. - * Pass `--straight-to-queue` to process the queue first before attempting to scrape. -* Command-line (`cmd`): an interactive mode for manual control and debugging (drops into Python interpretor) +### Example contents of `.env` without values +``` +scraper_username0= +scraper_password0= +scraper_username1= +scraper_password1= +scraper_username2= +scraper_password2= +scraper_username3= +scraper_password3= +web_auth_token= +app_key= +app_secret= +user_token= +user_secret= +``` *Created for the spirit of entertainment and in the name of unity.* ❤ From fbb58c352896f2176b97f3312ec72ffff7337aeb Mon Sep 17 00:00:00 2001 From: muskit <15199219+muskit@users.noreply.github.com> Date: Fri, 18 Aug 2023 02:12:29 -0700 Subject: [PATCH 18/18] remove fallback fetch date if catchup errors --- src/catchup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/catchup.py b/src/catchup.py index 3f42c14..79f5d5a 100644 --- a/src/catchup.py +++ b/src/catchup.py @@ -34,7 +34,6 @@ async def get_cross_tweets_online(): for i, (talent_id, talent_username) in enumerate(talent_lists.talents.items()): print(f'[{i+1}/{len(talent_lists.talents)}] {talent_username}-----------------------------------') try: - # tweets = get_user_tweets(talent_id, since_date=queue.finished_user_dates.get(talent_id, None)) since_date = queue.finished_user_dates.get(talent_id, None) ttweets = scraper.get_cross_ttweets_from_user(talent_username, since_date=since_date) print(f'got {len(ttweets)} TalentTweets') @@ -48,8 +47,6 @@ async def get_cross_tweets_online(): print('Error occurred processing tweet data.') safe_to_post_tweets = False traceback.print_exc() - if talent_id not in queue.finished_user_dates: - queue.finished_user_dates[talent_id] = '2023-04-26' # date is when bot token first got revoked else: queue.finished_user_dates[talent_id] = util.get_current_date() queue.save_file()