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

tweet_generator.Bot.twitter()   B

Complexity

Conditions 3

Size

Total Lines 72
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 72
rs 8.8
c 0
b 0
f 0
cc 3
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
import re
2
import textwrap
3
import os
4
import sys
5
from datetime import datetime
6
from PIL import Image, ImageFont, ImageDraw, ImageOps
7
from pincer import command, Client
8
from pincer.commands import CommandArg, Description
9
from pincer.objects import Message, Embed, MessageContext
10
11
12
# you need to manually download the font files and put them into the folder
13
# ./examples/tweet_generator/ to make the script works using this link:
14
# https://fonts.google.com/share?selection.family=Noto%20Sans:wght@400;700
15
if not all(
16
        font in os.listdir()
17
        for font in [
18
            "NotoSans-Regular.ttf",
19
            "NotoSans-Bold.ttf"
20
        ]
21
):
22
    sys.exit()
23
24
25
class Bot(Client):
26
    @Client.event
27
    async def on_ready(self):
28
        print(
29
            f"Started client on {self.bot}\n"
30
            f"Registered commands: {', '.join(self.chat_commands)}"
31
        )
32
33
    @command(description="to create fake tweets")
34
    async def twitter(
35
        self,
36
        ctx: MessageContext,
37
        content: CommandArg[str, Description["The content of the message"]]
38
    ):
39
        await ctx.interaction.ack()
40
41
        for text_match, user_id in re.findall(
42
            re.compile(r"(<@!(\d+)>)"), content
43
        ):
44
            content = content.replace(
45
                text_match, f"@{await self.get_user(user_id)}"
46
            )
47
48
        if len(content) > 280:
49
            return "A tweet can be at maximum 280 characters long"
50
51
        # download the profile picture and convert it into Image object
52
        avatar = (await ctx.author.user.get_avatar()).resize((128, 128))
53
        avatar = circular_avatar(avatar)
54
55
        # create the tweet by pasting the profile picture into a white image
56
        tweet = trans_paste(
57
            avatar,
58
            Image.new(
59
                "RGBA",
60
                (800, 250 + 50 * len(textwrap.wrap(content, 38))),
61
                (255, 255, 255)
62
            ),
63
            box=(15, 15),
64
        )
65
66
        # add the fonts
67
        font_normal = ImageFont.truetype("NotoSans-Regular.ttf", 40)
68
        font_small = ImageFont.truetype("NotoSans-Regular.ttf", 30)
69
        font_bold = ImageFont.truetype("NotoSans-Bold.ttf", 40)
70
71
        # write the name and username on the Image
72
        draw = ImageDraw.Draw(tweet)
73
        draw.text(
74
            (180, 20), str(ctx.author.user),
75
            fill=(0, 0, 0), font=font_bold
76
        )
77
        draw.text(
78
            (180, 70),
79
            f"@{ctx.author.user.username}",
80
            fill=(120, 120, 120), font=font_normal,
81
        )
82
83
        content = add_color_to_mentions(content)
84
85
        # write the text
86
        tweet = draw_multicolored_text(tweet, content, font_normal)
87
88
        # write the footer
89
        draw.text(
90
            (30, tweet.size[1] - 60),
91
            datetime.now().strftime(
92
                "%I:%M %p · %d %b. %Y · Twitter for Discord"
93
            ),
94
            fill=(120, 120, 120),
95
            font=font_small,
96
        )
97
98
        return Message(
99
            embeds=[
100
                Embed(title="Twitter for Discord").set_image(
101
                    url="attachment://image0.png"
102
                )
103
            ],
104
            attachments=[tweet],
105
        )
106
107
108
def trans_paste(fg_img, bg_img, box=(0, 0)):
109
    """
110
    https://stackoverflow.com/a/53663233/15485584
111
    paste an image into one another
112
    """
113
    fg_img_trans = Image.new("RGBA", fg_img.size)
114
    fg_img_trans = Image.blend(fg_img_trans, fg_img, 1.0)
115
    bg_img.paste(fg_img_trans, box, fg_img_trans)
116
    return bg_img
117
118
119
def circular_avatar(avatar):
120
    mask = Image.new("L", (128, 128), 0)
121
    draw = ImageDraw.Draw(mask)
122
    draw.ellipse((0, 0, 128, 128), fill=255)
123
    avatar = ImageOps.fit(avatar, mask.size, centering=(0.5, 0.5))
124
    avatar.putalpha(mask)
125
    return avatar
126
127
128
def add_color_to_mentions(message):
129
    """
130
    generate a dict to set were the text need to be in different colors.
131
    if a word starts with '@' it will be write in blue.
132
133
    Parameters
134
    ----------
135
    message: the text
136
137
    Returns
138
    -------
139
    a list with all colors selected
140
    example:
141
        [
142
            {'color': (0, 0, 0), 'text': 'hello world '},
143
            {'color': (0, 154, 234), 'text': '@drawbu'}
144
        ]
145
146
    """
147
    message = textwrap.wrap(message, 38)
148
    message = "\n".join(message).split(" ")
149
    result = []
150
    for word in message:
151
        wordlines = word.splitlines()
152
        for index, text in enumerate(wordlines):
153
154
            text += "\n" if index != len(wordlines) - 1 else " "
155
156
            if not result:
157
                result.append({"color": (0, 0, 0), "text": text})
158
                continue
159
160
            if not text.startswith("@"):
161
                if result[-1]["color"] == (0, 0, 0):
162
                    result[-1]["text"] += text
163
                    continue
164
165
                result.append({"color": (0, 0, 0), "text": text})
166
                continue
167
168
            result.append({"color": (0, 154, 234), "text": text})
169
    return result
170
171
172
def draw_multicolored_text(image, message, font):
173
    draw = ImageDraw.Draw(image)
174
    x = 30
175
    y = 170
176
    y_fontsize = font.getsize(" ")[1]
177
    for text in message:
178
        y -= y_fontsize
179
        for l_index, line in enumerate(text["text"].splitlines()):
180
            if l_index:
181
                x = 30
182
            y += y_fontsize
183
            draw.text((x, y), line, fill=text["color"], font=font)
184
            x += font.getsize(line)[0]
185
    return image
186
187
188
if __name__ == "__main__":
189
    # Of course we have to run our client, you can replace the
190
    # XXXYOURBOTTOKENHEREXXX with your token, or dynamically get it
191
    # through a dotenv/env.
192
    Bot("XXXYOURBOTTOKENHEREXXX").run()
193