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

tweet_generator.Bot.twitter()   D

Complexity

Conditions 12

Size

Total Lines 118
Code Lines 72

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 72
dl 0
loc 118
rs 4.2763
c 0
b 0
f 0
cc 12
nop 3

How to fix   Long Method    Complexity   

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:

Complexity

Complex classes like tweet_generator.Bot.twitter() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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