Skip to content

Commit

Permalink
Fix #5 (#6)
Browse files Browse the repository at this point in the history
* black format, bump version, add manual run harness

* Fix spacing

* lints, bugs
  • Loading branch information
jeffcasavant authored Mar 29, 2020
1 parent 56bf299 commit b4ad7cf
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 104 deletions.
2 changes: 1 addition & 1 deletion maubot.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
maubot: 0.1.0
id: casavant.jeff.trumptweet
version: 1.0.3
version: 1.0.4
license: MIT
modules:
- plugin
Expand Down
154 changes: 51 additions & 103 deletions plugin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#! /usr/bin/env python3

from datetime import datetime
from io import BytesIO
from os import listdir, path
from random import randrange, choice
from textwrap import wrap
import sys
import zipfile

from PIL import Image, ImageDraw, ImageFont
Expand All @@ -19,25 +22,19 @@ def list_avatars():

if BASE_PATH.endswith(".mbp"):
mbp = zipfile.ZipFile(BASE_PATH)
return [
path.basename(file.filename)
for file in mbp.infolist()
if file.filename.startswith(avatar_path)
]
else:
return listdir(avatar_path)
return [path.basename(file.filename) for file in mbp.infolist() if file.filename.startswith(avatar_path)]
return listdir(avatar_path)


def load_resource(path: str):
def load_resource(from_path: str):

if BASE_PATH.endswith(".mbp"):
mbp = zipfile.ZipFile(BASE_PATH)
return mbp.open(path)
else:
return open(path, "rb")
return mbp.open(from_path)
return open(from_path, "rb")


def human_fmt(n):
def format_number(n): # pylint: disable=invalid-name
for mag, abbr in [(1000000, "M"), (1000, "K")]:
if n > mag:
return "{:.1f}{}".format(n / mag, abbr)
Expand Down Expand Up @@ -66,42 +63,27 @@ def __init__(self, user, content):
self.replies = randrange(self.retweets) # nosec

@classmethod
def _draw_message_multicolored(cls, img, content, content_font, height):
def _draw_message_multicolored(cls, content, content_font, height, cursor):
text_color = "#14171a"
link_color = "#0084b4"
y_text = 70
fill = " @ "

tmp = Image.new("RGB", (2000, height), color="white") # scrap image
cursor = ImageDraw.Draw(tmp)
w_fill, y = cursor.textsize(fill, font=content_font)

for line in content:
_, height = content_font.getsize(line)
x_draw, x_paste = 0, cls._border
x_draw = cls._border
current_color = text_color
for char in line:
if char in ["@", "#"]:
current_color = link_color
elif current_color != text_color and not (
char.isalpha() or char == "_"
):
elif current_color != text_color and not (char.isalnum() or char == "_"):
current_color = text_color
w_full = cursor.textsize(fill + char, font=content_font)[0]
w = w_full - w_fill
cursor.text(
(x_draw, y_text), fill + char, font=content_font, fill=current_color
)
cut = tmp.crop(
(x_draw + w_fill, y_text + 1, x_draw + w_full, y_text + y)
)
img.paste(cut, (x_paste, y_text))
x_draw += w_full
x_paste += w
width, _ = cursor.textsize(char, font=content_font)
cursor.text((x_draw, y_text), char, font=content_font, fill=current_color)
x_draw += width - 1
y_text += height

def render(self):
content = wrap(self.content, width=50)
content = wrap(self.content, width=49)
height_addl = 24 * (len(content) - 1)

img = Image.new("RGB", (Tweet._width, 236 + height_addl), color="white")
Expand All @@ -116,22 +98,14 @@ def render(self):
cursor = ImageDraw.Draw(img)

# Draw username
displayname_font = ImageFont.truetype(
load_resource("res/font/Roboto-Black.ttf"), 15
)
cursor.text(
(70, 15), self.user.displayname, font=displayname_font, fill="#14171a"
)
username_font = ImageFont.truetype(
load_resource("res/font/Roboto-Regular.ttf"), 12
)
displayname_font = ImageFont.truetype(load_resource("res/font/Roboto-Black.ttf"), 15)
cursor.text((70, 15), self.user.displayname, font=displayname_font, fill="#14171a")
username_font = ImageFont.truetype(load_resource("res/font/Roboto-Regular.ttf"), 12)
cursor.text((70, 38), self.user.username, font=username_font, fill="#657786")

# Draw message
content_font = ImageFont.truetype(
load_resource("res/font/Roboto-Regular.ttf"), 22
)
self._draw_message_multicolored(img, content, content_font, img.height)
content_font = ImageFont.truetype(load_resource("res/font/Roboto-Regular.ttf"), 22)
self._draw_message_multicolored(content, content_font, img.height, cursor)

# Draw timestamp
date_font = ImageFont.truetype(load_resource("res/font/Roboto-Regular.ttf"), 12)
Expand All @@ -143,47 +117,29 @@ def render(self):
)

# Draw stats
highlight_stat_font = ImageFont.truetype(
load_resource("res/font/Roboto-Black.ttf"), 13
)
cursor.text(
(Tweet._border, 157 + height_addl),
"{:,}".format(self.retweets),
font=highlight_stat_font,
fill="#14171a",
)
stat_name_font = ImageFont.truetype(
load_resource("res/font/Roboto-Regular.ttf"), 13
)
highlight_stat_font = ImageFont.truetype(load_resource("res/font/Roboto-Black.ttf"), 13)
cursor.text(
(47, 157 + height_addl), "Retweets", font=stat_name_font, fill="#657786"
(Tweet._border, 157 + height_addl), "{:,}".format(self.retweets), font=highlight_stat_font, fill="#14171a",
)
stat_name_font = ImageFont.truetype(load_resource("res/font/Roboto-Regular.ttf"), 13)
cursor.text((47, 157 + height_addl), "Retweets", font=stat_name_font, fill="#657786")
cursor.text(
(110, 157 + height_addl),
"{:,}".format(self.likes),
font=highlight_stat_font,
fill="#14171a",
)
cursor.text(
(148, 157 + height_addl), "Likes", font=stat_name_font, fill="#657786"
(110, 157 + height_addl), "{:,}".format(self.likes), font=highlight_stat_font, fill="#14171a",
)
cursor.text((148, 157 + height_addl), "Likes", font=stat_name_font, fill="#657786")

# Draw layout
line_start = Tweet._border + 2
line_end = Tweet._width - line_start
topline_height = 145 + height_addl
bottomline_height = 185 + height_addl
vertical_width = 190
cursor.line((line_start, topline_height) + (line_end, topline_height), fill="lightgray")
cursor.line(
(line_start, topline_height) + (line_end, topline_height), fill="lightgray"
(vertical_width, topline_height) + (vertical_width, bottomline_height), fill="lightgray",
)
cursor.line(
(vertical_width, topline_height) + (vertical_width, bottomline_height),
fill="lightgray",
)
cursor.line(
(line_start, bottomline_height) + (line_end, bottomline_height),
fill="lightgray",
(line_start, bottomline_height) + (line_end, bottomline_height), fill="lightgray",
)

# Add side avatars
Expand All @@ -193,50 +149,33 @@ def render(self):
mask_draw = ImageDraw.Draw(small_avatar_mask)
mask_draw.ellipse((0, 0) + (20, 20), fill=255)
for index in range(10):
avatar = Image.open(
load_resource("res/img/avatars/{}".format(choice(avatars))) # nosec
).resize((20, 20))
avatar = Image.open(load_resource("res/img/avatars/{}".format(choice(avatars)))).resize((20, 20)) # nosec
img.paste(
avatar,
(avatar_base_width + index * 24, 157 + height_addl),
mask=small_avatar_mask,
avatar, (avatar_base_width + index * 24, 157 + height_addl), mask=small_avatar_mask,
)

# Add verification
if self.user.verified:
verified = Image.open(load_resource("res/img/verified.png")).resize(
(15, 15)
)
verified = Image.open(load_resource("res/img/verified.png")).resize((15, 15))
img.paste(verified, (187, 15), mask=verified)

# Add bottom stats summary
bottomstats_text_height = 202 + height_addl
reply = Image.open(load_resource("res/img/reply.png")).resize((20, 20))
img.paste(reply, (10, 198 + height_addl), mask=reply)
bottom_stat_font = ImageFont.truetype(
load_resource("res/font/Roboto-Black.ttf"), 12
)
bottom_stat_font = ImageFont.truetype(load_resource("res/font/Roboto-Black.ttf"), 12)
cursor.text(
(45, bottomstats_text_height),
human_fmt(self.replies),
font=bottom_stat_font,
fill="#657786",
(45, bottomstats_text_height), format_number(self.replies), font=bottom_stat_font, fill="#657786",
)
retweet = Image.open(load_resource("res/img/retweet.png")).resize((26, 16))
img.paste(retweet, (85, 200 + height_addl), mask=retweet)
cursor.text(
(125, bottomstats_text_height),
human_fmt(self.retweets),
font=bottom_stat_font,
fill="#657786",
(125, bottomstats_text_height), format_number(self.retweets), font=bottom_stat_font, fill="#657786",
)
like = Image.open(load_resource("res/img/like.png")).resize((20, 20))
img.paste(like, (165, 200 + height_addl), mask=like)
cursor.text(
(200, bottomstats_text_height),
human_fmt(self.likes),
font=bottom_stat_font,
fill="#657786",
(200, bottomstats_text_height), format_number(self.likes), font=bottom_stat_font, fill="#657786",
)

return img
Expand All @@ -252,18 +191,22 @@ def trump_user():
return user


def render_tweet(message):
trump = trump_user()
tweet = Tweet(trump, message)

img = tweet.render()
return img


class TrumpTweetPlugin(Plugin):
@command.new("trump")
@command.argument("message", pass_raw=True, required=True)
async def handler(self, evt: MessageEvent, message: str) -> None:
await evt.mark_read()
trump = trump_user()
tweet = Tweet(trump, message)

img = render_tweet(message)
file = BytesIO()
img = tweet.render()
img.save(file, format="png")

mxc_uri = await self.client.upload_media(file.getvalue(), mime_type="image/png")
content = MediaMessageEventContent()
info = ImageInfo()
Expand All @@ -275,3 +218,8 @@ async def handler(self, evt: MessageEvent, message: str) -> None:
content.url = mxc_uri

await evt.respond(content)


if __name__ == "__main__":
_message = " ".join(sys.argv[1:])
render_tweet(_message).show()

0 comments on commit b4ad7cf

Please sign in to comment.