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

tweet_generator.Bot.twitter()   D

Complexity

Conditions 12

Size

Total Lines 108
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 69
dl 0
loc 108
rs 4.3745
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
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
        avatar = (await ctx.author.user.get_avatar()).resize((128, 128))
37
38
        # modify profile picture to be circular
39
        mask = Image.new('L', (128, 128), 0)
40
        draw = ImageDraw.Draw(mask)
41
        draw.ellipse((0, 0) + (128, 128), fill=255)
42
        avatar = ImageOps.fit(avatar, mask.size, centering=(0.5, 0.5))
43
        avatar.putalpha(mask)
44
45
        # create the tweet by pasting the profile picture into a white image
46
        tweet = trans_paste(
47
            avatar,
48
49
            # background
50
            Image.new('RGBA', (800, 250 + 50 * len(message)), (255, 255, 255)),
51
52
            box=(15, 15)
53
        )
54
55
        # add the fonts
56
        font = ImageFont.truetype('Segoe UI.ttf', 40)
57
        font_small = ImageFont.truetype('Segoe UI.ttf', 30)
58
        font_bold = ImageFont.truetype('Segoe UI Bold.ttf', 40)
59
60
        # write the name and username on the Image
61
        draw = ImageDraw.Draw(tweet)
62
        draw.text((180, 20), str(ctx.author.user), fill=(0, 0, 0),
63
                  font=font_bold)
64
        draw.text((180, 70), '@' + ctx.author.user.username, fill=(120, 120, 120),
65
                  font=font)
66
67
        # write the content of the tweet on the Image
68
        message = '\n'.join(message).split(' ')
69
        result = []
70
71
        # generate a dict to set were the text need to be in different color.
72
        # for example, if a word starts with '@' it will be write in blue.
73
        # example:
74
        #   [
75
        #       {'color': (0, 0, 0), 'text': 'hello world '},
76
        #       {'color': (0, 154, 234), 'text': '@drawbu'}
77
        #   ]
78
        for word in message:
79
            for i_o, o in enumerate(word.split('\n')):
80
81
                o += '\n' if i_o != len(word.split('\n')) - 1 else ' '
82
83
                if not result:
84
                    result.append({'color': (0, 0, 0), 'text': o})
85
                    continue
86
87
                if not o.startswith('@'):
88
                    if result[-1:][0]['color'] == (0, 0, 0):
89
                        result[-1:][0]['text'] += o
90
                        continue
91
92
                    result.append({'color': (0, 0, 0), 'text': o})
93
                    continue
94
95
                result.append({'color': (0, 154, 234), 'text': o})
96
97
        # write the text
98
        draw = ImageDraw.Draw(tweet)
99
        x = 30
100
        y = 170
101
        for o in result:
102
            y -= font.getsize(' ')[1]
103
            for l_index, line in enumerate(o['text'].split('\n')):
104
                if l_index != 0:
105
                    x = 30
106
                y += font.getsize(' ')[1]
107
                draw.text((x, y), line, fill=o['color'], font=font)
108
                x += font.getsize(line)[0]
109
110
        # write the footer
111
        draw.text(
112
            (30, tweet.size[1] - 60),
113
            datetime.now().strftime(
114
                '%I:%M %p · %d %b. %Y · Twitter for Discord'),
115
            fill=(120, 120, 120),
116
            font=font_small)
117
118
        return Message(
119
            embeds=[
120
                Embed(
121
                    title='Twitter for Discord',
122
                    description=''
123
                ).set_image(url="attachment://image0.png")
124
            ],
125
            attachments=[tweet]
126
        )
127
128
129
# https://stackoverflow.com/a/53663233/15485584
130
def trans_paste(fg_img, bg_img, alpha=1.0, box=(0, 0)):
131
    """
132
    paste an image into one another
133
    """
134
    fg_img_trans = Image.new("RGBA", fg_img.size)
135
    fg_img_trans = Image.blend(fg_img_trans, fg_img, alpha)
136
    bg_img.paste(fg_img_trans, box, fg_img_trans)
137
    return bg_img
138
139
140
if __name__ == "__main__":
141
    # Of course we have to run our client, you can replace the
142
    # XXXYOURBOTTOKENHEREXXX with your token, or dynamically get it
143
    # through a dotenv/env.
144
    Bot("XXXYOURBOTTOKENHEREXXX").run()
145