Browse Source

Init

master
NaiJi 10 months ago
commit
a4cd71b4cb
  1. 4
      .gitignore
  2. 24
      LICENSE
  3. 26
      README.md
  4. 8
      emojis.dat
  5. 47
      loadurls.py
  6. 69
      quotes.dat
  7. 20
      requirements.txt
  8. 123
      trixieanswer.py
  9. 69
      trixiepost.py

4
.gitignore

@ -0,0 +1,4 @@
token.dat
lastid.dat
screens.dat
venv

24
LICENSE

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

26
README.md

@ -0,0 +1,26 @@
# trixie-bot
The Great and Powerful Trixie Lulabot for Mastodon! Absolutely required to be a part of your everyday instance timeline!
Currently posting on: https://social.inex.rocks/@TrixieLulamoon
- - - -
What she does:
- [x] Posting random screenshots of her
- [x] Answering on mentions and timeline pings
- [x] Blessing your life
- [ ] Being useless
You are not cloning yet?
![alt text](https://social.inex.rocks/system/custom_emojis/images/000/000/180/original/trixiehehe.png?1566425647)
### How to run ###
* Download screenshots URLs:
```bash
python3 loadurls.py
```
* To make a single post with a random screenshot, run `trixiepost.py`
* To check the notifications and reply to new ones, run `trixieanswer.py`
* Fill `emojis.dat` file with emojis you want Trixie to use (she picks them randomly)!
* Don't forget to setup your bot account, get access tokens and establish the environment around it. I am not providing any instructions of how to do it, since all the steps are different and depend on what, where and how you want to run the bot.
* That's all!
![alt text](https://i.imgur.com/jZaXxMT.png)

8
emojis.dat

@ -0,0 +1,8 @@
:trixiehappy:
:trixiehehe:
:trixiehat:
:trixieboop:
:trixiesmile:
:trixiewhatever:
:trixiemoi:
:trixieangel:

47
loadurls.py

@ -0,0 +1,47 @@
import sys
import os.path as op
from urllib.parse import urlparse
import requests
from bs4 import BeautifulSoup
URLs = [
r'https://mlp.fandom.com/wiki/Trixie/Gallery/Seasons_1-5',
r'https://mlp.fandom.com/wiki/Trixie/Gallery/Season_6',
r'https://mlp.fandom.com/wiki/Trixie/Gallery', # Season 7 and higher are on the main gallery page
]
screenshots_urls = []
def dispense(s, nbeg, nend):
resp = requests.get(s)
soup = BeautifulSoup(resp.text, 'lxml')
# Season screenshots are stored in gallery-i tags
for i in range(nbeg, nend):
gallery_tag = soup.find(id=f'gallery-{i}')
# Extracting the image url from a needed child
for child in gallery_tag.find_all(class_='wikia-gallery-item'):
fullpath = urlparse(child.img.get('src'))
flavor = urlparse(child.img.get('alt'))
path = fullpath.path
while not (path.endswith('.png') or path.endswith('.jpg')):
path = op.dirname(path)
screenshots_urls.append(f'{fullpath.scheme}://{fullpath.netloc}{path}')
screenshots_urls.append(flavor.path)
def main():
dispense(URLs[0], 0, 5) # Seasons 1-5
dispense(URLs[1], 0, 3) # Season 6
dispense(URLs[2], 5, 15) # Seasons 7+
with open('screens.dat', 'w', encoding='utf-8') as file:
for one_url in screenshots_urls:
print(one_url, file=file)
if __name__ == '__main__':
sys.exit(main())

69
quotes.dat

@ -0,0 +1,69 @@
"I hereby challenge you: anything you can do, I can do better. So? Or is Trixie destined to be the greatest equine who has ever lived!?!"
"You will never have the amazing, show-stopping ability of the Great and Powerful Trixie!"
"Trixie is the highest level unicorn!"
"The Great and Powerful Trixie doesn't trust wheels."
"Moi?"
"I treated my friends so horribly when I was wearing that Alicorn Amulet... I just couldn't control myself. Can you forgive me?"
"Don't you think the Great and Apologetic Trixie is the most magnificent humble pony you have ever seen?"
"Hello!"
"Everypony always says they'll give you a second chance, but deep down, they never forget."
"I definitely don't feel as if my heart is breaking into a million pieces!"
"Come one, come all. Come and see the Pathetic and Friendless Trixie's "Way-to-Go-Dum-Dum-You-Really-Messed-It-Up-This-Time Repentance Tour"."
"Behold, the Peat and Growerful Triskie...!"
"I'd love to perform for peanut butter crackers..."
"You don't need magic to figure out what to do next, you are really good! Listen to your best friend."
"The Starlight I love is passionate, lively, and yeah, sometimes angry. Those are my favorite parts of her. That and he fact that she forgives me every time."
"Behold, visiting friend from Starlight's past, and be amazed by the Great and Powerful – and current best friend of Starlight – Trixie!"
"No fruit calls in my timeline!"
"Usually, ponies just call me Trixie. But "Ms. Powerful" has a nice ring."
"You know, I would hate traveling with myself."
"Everypony deserves a second chance—even a third chance!"
"And with that, the Great and Powerful Trixie saves the day!"
"I swear. Everypony thinks I'm bad, but... I'm trying so hard to be good."
"You reminded me that we don't have to be bad forever. Everypony deserves a second chance."
"It's good to be the queen!"
"Once the unlucky ponies who get a cup of 'cider' from this barrel take a sip, I'll swoop in and perform a magnificent spell to save them from the horrible taste!"
"The Great and Powerful Trixie doesn't have all day!"
"Trixie will go with you!"
"Once I had a book and I had no idea it was so important! I actually kept it in case it was magic but before I could read it, Discord borrowed it... without asking!"
"The Great and Powerful Trixie doesn't know anything about a giant ball of magic fireworks."
"Watch in AWE!"
"How about... what?"
"*hugs*"
"The Great and Powerful Trixie has proven herself to be the most amazing unicorn in all of Equestria. Huh, was there ever any doubt?"
"The Great and Powerful Trixie apologies but she did not want to be disturbed!"
"Once Trixie had to take a job on a rock farm just to earn a living! A rock farm!!!"
"*removes your mouth*"
"Well, well, well. If it isn't... you."
"Excellent!"
"Nopony's more powerful than the Great and Powerful Trixie!"
"Sometimes I still think about that pony with the ten instruments."
"The Grrrreat and Powerful Trrrrixie has come to perform a new stage show of grand illusion! I am calling it "The Humble and Penitent Trixie's Equestrian Apology Tour"! ... It's a working title."
"My next magic show's gonna be the greatest thing your timeline ever seen!"
"Everypony always says they'll give you a second chance, but deep down, they never forget."
"I heard what others said about me, and sometimes they're right. I wasn't very nice. So I'd understand if you didn't want to be friends."
"if you woke me up to play guessing games—"
"You know, figuring out the best way to do something before you actually do it."
"Okay. I am definitely glad you are here."
"How can Trixie help?"
"It was the Great and Powerful Trrrrixie's pleasure to save you from your imminent doom."
"Come on, come on! Turn into a teacup! ✨ ✨ ... No! Teacup! ✨ ... No! How come it's not working?"
"Teacup! Teacup, teacup, teacup! Tea-cup! Teacup, teacup, tea-cup! You know what you need? A teacup!"
"Real magic! Come on! Be impressed by me! "Yay, Trixie! You're so great at magic and having good hair!""
"Are you okay? Because you seem a little, uh, what's the word... "lch-bh-ba"."
"Um, why are they looking at Trixie like that...?"
"Uh-huh. You're just trying to scare me, but it won't work. Because not only am I the Great and Powerful, I am also the Unscareable Trixie!"
"You know, you're a lot like Trixie!"
"W-w... Mmmmmmmaybe?"
"Hello, friend! You may call me the Great and Powerful Professor Trrrrrrixie"
"Trixie does require a grrrreat and powerful assistant."
"The Great and Powerful Trixie doesn't chant."
"Trixie is a traveling magician after all!!"
"Well, all right then! I guess it's time to hit the road! :trhappy: "
"Well, when you put it like that, Trixie is actually kind of excited!"
"Sure is great chatting with you, buddy!"
"Is there something I can do to make things more comfortable for you?"
"Prepare to be amazed..."
"Okay! Fine! We're friends who share a deep bond who weren't prepared for the emotional challenges of friendship."
"Magic, tra-magic... poof of smoke... want to wave your hooves... and tell a... little joke!"
"I think it's made our friendship greater and more powerful than ever!"

20
requirements.txt

@ -0,0 +1,20 @@
requests==2.22.0
requests-oauthlib==1.2.0
beautifulsoup4==4.8.0
certifi==2019.6.16
chardet==3.0.4
decorator==4.4.0
DerPyBooru==0.7.3
idna==2.8
lxml==4.3.4
Mastodon.py==1.4.6
oauthlib==3.0.2
PySocks==1.7.0
python-dateutil==2.8.0
python-docx==0.8.10
python-magic==0.4.15
pytz==2019.2
six==1.12.0
soupsieve==1.9.2
tweepy==3.8.0
urllib3==1.25.3

123
trixieanswer.py

@ -0,0 +1,123 @@
import sys
import random
import datetime
import os.path as op
from pprint import pprint
import requests
from mastodon import Mastodon
# To avoid obvious repetitivity --------------------
def get_rand_int(l_int, rrang) -> int:
tmp = random.randint(0, rrang)
while tmp == l_int:
tmp = random.randint(0, rrang)
return tmp
# Compare time -------------------------------------
def is_actual(time_date_now, time_date_posted) -> bool:
time_date_posted = time_date_posted.replace('T', ' ')
# If not the same day:
if time_date_now.split()[0] != time_date_posted.split()[0]:
return False
time_grades_now = time_date_now.split()[1].split(':')
time_grades_posted = time_date_posted.split()[1].split(':')
# If different hours:
if time_grades_now[0] != time_grades_posted[0]:
return (time_grades_now[1] == '00' or time_grades_now[1] == '01'
and time_grades_posted[1] == '58' or time_grades_posted[1] == '59')
# If same hours, difference shouldn't be more than 3 minutes
return abs(int(time_grades_now[1]) - int(time_grades_posted[1])) < 3
# --------------------------------------------------
def main():
mastodon = Mastodon(
access_token = 'token.dat',
api_base_url = 'https://social.inex.rocks/'
)
# Quotes for mentions
if op.isfile('quotes.dat'):
with open('quotes.dat', 'r', encoding='utf-8') as file:
quotes = file.readlines()
else:
time = str(datetime.datetime.now().time())
print(time, ': Quotes disappeared!')
quotes = [ 'All my quotes are gone! Please, contact @NaiJi!' ]
# Emojis for each of her toot
if op.isfile('emojis.dat'):
with open('emojis.dat', 'r', encoding='utf-8') as file:
emojis = file.readlines()
else:
ttime = str(datetime.datetime.now().time())
print(ttime, ': Emojis disappeared!')
emojis = [ ':trixiehappy:' ]
# The latest mentioned user
if op.isfile('lastid.dat'):
with open('lastid.dat', 'r', encoding='utf-8') as file:
last_id = int(file.readline())
else:
ttime = str(datetime.datetime.now().time())
print(ttime, ': lastid.dat is missing. Creating.')
with open('lastid.dat', 'w', encoding='utf-8') as file:
last_id = -1
is_first = True
newest_id = last_id
last_emoji = ':?:'
last_quote = -1
# Iterating the last 5 mentions and answering on them until Trixie finds already mentioned id
try:
for entry in mastodon.notifications(limit=5):
if entry.get('type') == 'mention':
# if the toot is too old, ignore it!
if is_actual(str(datetime.datetime.now()), entry.get('created_at')):
continue
current_id = int(entry.get('id'))
visibility = entry.get('visibility')
if current_id != last_id:
username = entry.get('account').get('username')
last_emoji = get_rand_int(last_emoji, len(emojis) - 1)
last_quote = get_rand_int(last_quote, len(quotes) - 1)
mastodon.status_reply(entry.get('status'),
f'{quotes[last_quote][1:-2]} {emojis[last_emoji]}',
media_ids=None, visibility=visibility)
else:
if (is_first):
newest_id = current_id
break
# The first examined mention is the last written mention, so we must save its id
if (is_first):
is_first = False
newest_id = current_id
except KeyboardInterrupt:
sys.exit(1)
except:
ttime = str(datetime.datetime.now().time())
print(ttime, ': Error while mentioning!')
# The latest mentioned user. If it disappeared after posting, it's super weird!
if op.isfile('lastid.dat'):
with open('lastid.dat', 'w', encoding='utf-8') as file:
print(newest_id, end='', file=file)
else:
ttime = str(datetime.datetime.now().time())
print(ttime, ': lastid.dat is missing AFTER answering! Creating the file. Be aware of next Trixie\'s double mentioning.')
with open('lastid.dat', 'w', encoding='utf-8') as file:
last_id = -1
if __name__ == '__main__':
sys.exit(main())

69
trixiepost.py

@ -0,0 +1,69 @@
import sys
import random
import datetime
import os.path as op
from pprint import pprint
import requests
from mastodon import Mastodon
# --------------------------------------------------
def main():
mastodon = Mastodon(
access_token = 'token.dat',
api_base_url = 'https://social.inex.rocks/'
)
if op.isfile('screens.dat'):
with open('screens.dat', 'r', encoding='utf-8') as file:
data = file.readlines()
else:
ttime = str(datetime.datetime.now().time())
url = 'https://vignette.wikia.nocookie.net/mlp/images/c/c7/Trixie_sighing_sadly_S6E6.png'
print(ttime, ': screens.dat is missing! Please, generate new sources with :/parses.py!')
try:
media = mastodon.media_post(requests.get(url).content, f'image/png')
toot = f'My sources are missing! Please, contanct @NaiJi!\n:trixiesweating::trixiesweating::trixiesweating:'
mastodon.status_post(toot, media_ids=[media], visibility='unlisted')
except KeyboardInterrupt:
sys.exit(1)
except:
ttime = str(datetime.datetime.now().time())
print(ttime, ': Network error while posting a screenshot without the source file')
return
# Emojis for each of her toot
if op.isfile('emojis.dat'):
with open('emojis.dat', 'r', encoding='utf-8') as file:
emojis = file.readlines()
else:
ttime = str(datetime.datetime.now().time())
print(ttime, ': Emojis disappeared!')
emojis = [ ':trixiehappy:' ]
rnum = random.randint(0, (len(data) - 2) / 2)
url = data[rnum * 2].strip()
status = data[rnum * 2 + 1].strip()
fformat = op.splitext(url)[1][1:]
emoji = emojis[random.randint(0, len(emojis) - 1)]
try:
media = mastodon.media_post(requests.get(url).content, f'image/{fformat}')
toot = f'{status}\n#MLP #MLPFIM #MLPTrixie {emoji}'
mastodon.status_post(toot, media_ids=[media], visibility='unlisted')
except KeyboardInterrupt:
sys.exit(1)
except:
ttime = str(datetime.datetime.now().time())
print(ttime, ': Network error while posting a screenshot!')
if __name__ == '__main__':
sys.exit(main())
Loading…
Cancel
Save