Passed
Pull Request — main (#241)
by Yohann
01:42
created

tweet_generator.trans_paste()   A

Complexity

Conditions 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nop 4
1
import re
2
import textwrap
3
from datetime import datetime
4
from PIL import Image, ImageFont, ImageDraw, ImageOps
5
from pincer import command, Client, Descripted
6
from pincer.objects import Message, Embed, MessageContext
7
8
9
class Bot(Client):
10
11
    @Client.event
12
    async def on_ready(self):
13
        print(
14
            f"Started client on {self.bot}\n"
15
            "Registered commands: " + ", ".join(self.chat_commands)
16
        )
17
18
    @command(
19
        name='twitter',
20
        description='to create fake tweets',
21
        guild=690604075775164437
22
    )
23
    async def twitter(self, ctx: MessageContext, content: Descripted[str, '...']):
24
        await ctx.interaction.ack()
25
26
        message = content
27
        for text_match, user_id in re.findall(re.compile(r"(<@!(\d+)>)"), message):
28
            message = message.replace(text_match, '@' + str(await self.get_user(user_id)))
29
30
        if len(message) > 280:
31
            return 'A tweet can be at maximum 280 characters long'
32
33
        # wrap the message to be multi-line
34
        message = textwrap.wrap(message, 38)
35
36
        # download the profile picture and convert it into Image object
37
        avatar = await self.get_user(ctx.author.user.id)
38
        avatar = await avatar.get_avatar()
39
        avatar = avatar.resize((128, 128))
40
41
        # modify profile picture to be circular
42
        mask = Image.new('L', (128, 128), 0)
43
        draw = ImageDraw.Draw(mask)
44
        draw.ellipse((0, 0) + (128, 128), fill=255)
45
        avatar = ImageOps.fit(avatar, mask.size, centering=(0.5, 0.5))
46
        avatar.putalpha(mask)
47
48
        # create the tweet by pasting the profile picture into a white image
49
        tweet = trans_paste(
50
            avatar,
51
52
            # background
53
            Image.new('RGBA', (800, 250 + 50 * len(message)), (255, 255, 255)),
54
55
            box=(15, 15)
56
        )
57
58
        # add the fonts
59
        font = ImageFont.truetype('Segoe UI.ttf', 40)
60
        font_small = ImageFont.truetype('Segoe UI.ttf', 30)
61
        font_bold = ImageFont.truetype('Segoe UI Bold.ttf', 40)
62
63
        # write the name and username on the Image
64
        draw = ImageDraw.Draw(tweet)
65
        draw.text((180, 20), str(ctx.author.user), fill=(0, 0, 0),
66
                  font=font_bold)
67
        draw.text((180, 70), '@' + ctx.author.user.username, fill=(120, 120, 120),
68
                  font=font)
69
70
        # write the content of the tweet on the Image
71
        message = '\n'.join(message).split(' ')
72
        result = []
73
74
        # generate a dict to set were the text need to be in different color.
75
        # for example, if a word starts with '@' it will be write in blue.
76
        # example:
77
        #   [
78
        #       {'color': (0, 0, 0), 'text': 'hello world '},
79
        #       {'color': (0, 154, 234), 'text': '@drawbu'}
80
        #   ]
81
        for word in message:
82
            for i_o, o in enumerate(word.split('\n')):
83
84
                o += '\n' if i_o != len(word.split('\n')) - 1 else ' '
85
86
                if not result:
87
                    result.append({'color': (0, 0, 0), 'text': o})
88
                    continue
89
90
                if not o.startswith('@'):
91
                    if result[-1:][0]['color'] == (0, 0, 0):
92
                        result[-1:][0]['text'] += o
93
                        continue
94
95
                    result.append({'color': (0, 0, 0), 'text': o})
96
                    continue
97
98
                result.append({'color': (0, 154, 234), 'text': o})
99
100
        # write the text
101
        draw = ImageDraw.Draw(tweet)
102
        x = 30
103
        y = 170
104
        for o in result:
105
            y -= font.getsize(' ')[1]
106
            for l_index, line in enumerate(o['text'].split('\n')):
107
                if l_index != 0:
108
                    x = 30
109
                y += font.getsize(' ')[1]
110
                draw.text((x, y), line, fill=o['color'], font=font)
111
                x += font.getsize(line)[0]
112
113
        # write the footer
114
        draw.text(
115
            (30, tweet.size[1] - 60),
116
            datetime.now().strftime(
117
                '%I:%M %p · %d %b. %Y · Twitter for Discord'),
118
            fill=(120, 120, 120),
119
            font=font_small)
120
121
        return Message(
122
            embeds=[
123
                Embed(
124
                    title='Twitter for Discord',
125
                    description=''
126
                ).set_image(url="attachment://image0.png")
127
            ],
128
            attachments=[tweet]
129
        )
130
131
132
# https://stackoverflow.com/a/53663233/15485584
133
def trans_paste(fg_img, bg_img, alpha=1.0, box=(0, 0)):
134
    """
135
    paste an image into one another
136
    """
137
    fg_img_trans = Image.new("RGBA", fg_img.size)
138
    fg_img_trans = Image.blend(fg_img_trans, fg_img, alpha)
139
    bg_img.paste(fg_img_trans, box, fg_img_trans)
140
    return bg_img
141
142
143
if __name__ == "__main__":
144
    # Of course we have to run our client, you can replace the
145
    # XXXYOURBOTTOKENHEREXXX with your token, or dynamically get it
146
    # through a dotenv/env.
147
    Bot("XXXYOURBOTTOKENHEREXXX").run()
148