Passed
Pull Request — main (#241)
by
unknown
01:46
created

tweet_generator.Bot.on_ready()   A

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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