Skip to content

Commit 8b116f5

Browse files
authored
Merge pull request #1 from commonlispbr/feature/tests
Add unit tests and CI config
2 parents 2c092c2 + c5056f0 commit 8b116f5

File tree

4 files changed

+311
-79
lines changed

4 files changed

+311
-79
lines changed

.github/workflows/test.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
on: [push, pull_request]
2+
name: test
3+
jobs:
4+
lint:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- name: Install Go
8+
uses: actions/setup-go@v1
9+
with:
10+
go-version: 1.13.x
11+
- name: Checkout code
12+
uses: actions/checkout@v1
13+
- name: Install golangci-lint
14+
run: |
15+
go get github.com/golangci/golangci-lint/cmd/golangci-lint
16+
- name: Run linters
17+
run: |
18+
export PATH=$PATH:$(go env GOPATH)/bin
19+
golangci-lint -E bodyclose,misspell,gocyclo,dupl,gofmt,golint,unconvert,goimports,depguard,gocritic,funlen,interfacer run
20+
test:
21+
strategy:
22+
matrix:
23+
go-version: [1.13.x, 1.14.x]
24+
platform: [ubuntu-latest]
25+
runs-on: ${{ matrix.platform }}
26+
steps:
27+
- name: Install Go
28+
if: success()
29+
uses: actions/setup-go@v1
30+
with:
31+
go-version: ${{ matrix.go-version }}
32+
- name: Checkout code
33+
uses: actions/checkout@v1
34+
- name: Run tests
35+
run: go test -v -covermode=count
36+
37+
coverage:
38+
runs-on: ubuntu-latest
39+
steps:
40+
- name: Install Go
41+
if: success()
42+
uses: actions/setup-go@v1
43+
with:
44+
go-version: 1.13.x
45+
- name: Checkout code
46+
uses: actions/checkout@v1
47+
- name: Calc coverage
48+
run: |
49+
export PATH=$PATH:$(go env GOPATH)/bin
50+
go test -v -covermode=count -coverprofile=coverage.out
51+
- name: Convert coverage to lcov
52+
uses: jandelgado/gcov2lcov-action@v1.0.0
53+
with:
54+
infile: coverage.out
55+
outfile: coverage.lcov
56+
- name: Codecov
57+
uses: codecov/codecov-action@v1
58+
with:
59+
token: ${{ secrets.CODECOV_TOKEN }}
60+
file: ./coverage.lcov
61+
flags: unittests
62+
fail_ci_if_error: true

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
![test](https://github.com/commonlispbr/troll-shield/workflows/test/badge.svg)
2+
![goreleaser](https://github.com/commonlispbr/troll-shield/workflows/goreleaser/badge.svg)
3+
[![codecov](https://codecov.io/gh/commonlispbr/troll-shield/branch/master/graph/badge.svg?token=8YZDC9BGZX)](https://codecov.io/gh/commonlispbr/troll-shield)
14
# Usage
25

36
``` bash

troll_shield.go

Lines changed: 109 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@ import (
1212
"strings"
1313
"sync"
1414

15-
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
15+
telegram "github.com/go-telegram-bot-api/telegram-bot-api"
1616
)
1717

18+
// TrollShieldBot aggregate the methods used by my bot to keep mocking easier
19+
type TrollShieldBot interface {
20+
GetChatMember(telegram.ChatConfigWithUser) (telegram.ChatMember, error)
21+
KickChatMember(telegram.KickChatMemberConfig) (telegram.APIResponse, error)
22+
Send(telegram.Chattable) (telegram.Message, error)
23+
}
24+
1825
// blacklist groups, member from that groups will be kicked automatically
1926
var trollGroups = []string{
2027
"@ccppbrasil",
@@ -31,16 +38,78 @@ const logfile = "troll-shield.log"
3138

3239
var log = logger.New(os.Stderr, "", logger.LstdFlags)
3340

34-
// findTrollHouse return the troll house group name if is well-known
35-
// otherwise, returns a empty string
36-
func findTrollHouses(bot *tgbotapi.BotAPI, userID int) string {
41+
// messageEvent return true if is a message event
42+
func messageEvent(update *telegram.Update) bool {
43+
return update.Message != nil
44+
}
45+
46+
// newChatMemberEvent return true if a new member joined to chat
47+
func newChatMemberEvent(update *telegram.Update) bool {
48+
return messageEvent(update) && update.Message.NewChatMembers != nil
49+
}
50+
51+
// fromChatEvent return true if the message is from a specific chat
52+
func fromChatEvent(update *telegram.Update, username string) bool {
53+
chat := update.Message.Chat
54+
return messageEvent(update) && chat != nil && (chat.UserName == username || chat.Title == username)
55+
}
56+
57+
// getUserName return the most meaningful name available from a telegram user
58+
// if it has a @username, return it
59+
// if not, try to return FirstName + LastName
60+
// otherwise, only return the FirstName
61+
func getUserName(user telegram.User) string {
62+
username := user.FirstName
63+
if user.UserName != "" {
64+
username = fmt.Sprintf("@%v", user.UserName)
65+
} else if user.LastName != "" {
66+
username = fmt.Sprintf("%s %s", user.FirstName, user.LastName)
67+
}
68+
return username
69+
}
70+
71+
func getUpdates(bot *telegram.BotAPI) telegram.UpdatesChannel {
72+
u := telegram.NewUpdate(0)
73+
u.Timeout = 60
74+
updates, err := bot.GetUpdatesChan(u)
75+
if err != nil {
76+
log.Printf("getUpdates error: %v", err)
77+
}
78+
79+
return updates
80+
}
81+
82+
func reply(bot TrollShieldBot, update *telegram.Update, text string) {
83+
msg := telegram.NewMessage(update.Message.Chat.ID, text)
84+
msg.ReplyToMessageID = update.Message.MessageID
85+
86+
_, err := bot.Send(msg)
87+
if err != nil {
88+
log.Printf("[!] Send msg failed: %v", err)
89+
}
90+
}
91+
92+
func welcomeMessage(bot TrollShieldBot, update *telegram.Update, member telegram.User) {
93+
username := getUserName(member)
94+
text := fmt.Sprintf(
95+
`Olá %s! Seja bem-vindo ao grupo oficial de Common Lisp do Brasil.
96+
Leia as regras em: https://lisp.com.br/rules.html.`,
97+
username,
98+
)
99+
reply(bot, update, text)
100+
}
101+
102+
// findTrollHouses return a string with groups separeted by comma,
103+
// that groups are well-known to being troll houses.
104+
// otherwise, if nothing is found returns a empty string
105+
func findTrollHouses(bot TrollShieldBot, userID int) string {
37106
ch := make(chan string, len(trollGroups))
38107
var wait sync.WaitGroup
39108
for _, trollGroup := range trollGroups {
40109
wait.Add(1)
41110
go func(group string) {
42111
defer wait.Done()
43-
c, _ := bot.GetChatMember(tgbotapi.ChatConfigWithUser{
112+
c, _ := bot.GetChatMember(telegram.ChatConfigWithUser{
44113
SuperGroupUsername: group,
45114
UserID: userID,
46115
})
@@ -66,20 +135,29 @@ func findTrollHouses(bot *tgbotapi.BotAPI, userID int) string {
66135
return strings.Join(houses, ", ")
67136
}
68137

69-
// messageEvent: return true if is a message event
70-
func messageEvent(update *tgbotapi.Update) bool {
71-
return update.Message != nil
72-
}
73-
74-
// newChatMemberEvent: return true if a new member joined to chat
75-
func newChatMemberEvent(update *tgbotapi.Update) bool {
76-
return messageEvent(update) && update.Message.NewChatMembers != nil
77-
}
78-
79-
// fromChatEvent: return true if the message is from a specific chat
80-
func fromChatEvent(update *tgbotapi.Update, username string) bool {
81-
chat := update.Message.Chat
82-
return messageEvent(update) && chat != nil && (chat.UserName == username || chat.Title == username)
138+
// kickTroll ban the troll and send a message about where we can found the trolls
139+
func kickTroll(bot TrollShieldBot, update *telegram.Update, user telegram.User, trollHouse string) {
140+
chatMember := telegram.ChatMemberConfig{
141+
ChatID: update.Message.Chat.ID,
142+
UserID: user.ID,
143+
}
144+
resp, err := bot.KickChatMember(
145+
telegram.KickChatMemberConfig{ChatMemberConfig: chatMember},
146+
)
147+
148+
if !resp.Ok || err != nil {
149+
log.Printf(
150+
"[!] Kicking %q did not work, error code %v: %v",
151+
user.FirstName, resp.ErrorCode, resp.Description,
152+
)
153+
} else {
154+
username := getUserName(user)
155+
text := fmt.Sprintf(
156+
"%v foi banido porque é membro do grupo: %v. Adeus.",
157+
username, trollHouse,
158+
)
159+
reply(bot, update, text)
160+
}
83161
}
84162

85163
func setupLogging() {
@@ -92,39 +170,18 @@ func setupLogging() {
92170

93171
log.SetOutput(wrt)
94172
// register log to BotLoggt
95-
err = tgbotapi.SetLogger(log)
173+
err = telegram.SetLogger(log)
96174
if err != nil {
97175
log.Printf("Set Telegram Bot Logging error: %v", err)
98176
}
99177
}
100178

101-
func getUserName(user tgbotapi.User) string {
102-
username := user.FirstName
103-
if user.UserName != "" {
104-
username = fmt.Sprintf("@%v", user.UserName)
105-
} else if user.LastName != "" {
106-
username = fmt.Sprintf("%s %s", user.FirstName, user.LastName)
107-
}
108-
return username
109-
}
110-
111-
func reply(bot *tgbotapi.BotAPI, update *tgbotapi.Update, text string) {
112-
msg := tgbotapi.NewMessage(update.Message.Chat.ID, text)
113-
msg.ReplyToMessageID = update.Message.MessageID
114-
115-
_, err := bot.Send(msg)
116-
if err != nil {
117-
log.Printf("[!] Send msg failed: %v", err)
118-
}
119-
}
120-
121-
func main() {
122-
setupLogging()
179+
func setupBot() *telegram.BotAPI {
123180
token, exists := os.LookupEnv("TELEGRAM_BOT_TOKEN")
124181
if !exists {
125182
log.Fatal("TELEGRAM_BOT_TOKEN env should be defined.")
126183
}
127-
bot, err := tgbotapi.NewBotAPI(token)
184+
bot, err := telegram.NewBotAPI(token)
128185

129186
if err != nil {
130187
log.Panic(err)
@@ -134,12 +191,13 @@ func main() {
134191

135192
log.Printf("Authorized on account @%s", bot.Self.UserName)
136193

137-
u := tgbotapi.NewUpdate(0)
138-
u.Timeout = 60
139-
140-
updates, err := bot.GetUpdatesChan(u)
194+
return bot
195+
}
141196

142-
for update := range updates {
197+
func main() {
198+
setupLogging()
199+
bot := setupBot()
200+
for update := range getUpdates(bot) {
143201
if messageEvent(&update) {
144202
if update.Message.Text == "/lelerax" {
145203
reply(bot, &update, "Estou vivo.")
@@ -149,37 +207,9 @@ func main() {
149207
if newChatMemberEvent(&update) {
150208
for _, member := range *update.Message.NewChatMembers {
151209
if trollHouse := findTrollHouses(bot, member.ID); trollHouse != "" {
152-
chatMember := tgbotapi.ChatMemberConfig{
153-
ChatID: update.Message.Chat.ID,
154-
UserID: member.ID,
155-
}
156-
resp, err := bot.KickChatMember(
157-
tgbotapi.KickChatMemberConfig{ChatMemberConfig: chatMember},
158-
)
159-
160-
if resp.Ok == false || err != nil {
161-
log.Printf(
162-
"[!] Kicking %q did not work, error code %v: %v",
163-
member.FirstName, resp.ErrorCode, resp.Description,
164-
)
165-
} else {
166-
username := getUserName(member)
167-
text := fmt.Sprintf(
168-
"%v foi banido porque é membro do grupo: %v. Adeus.",
169-
username, trollHouse,
170-
)
171-
reply(bot, &update, text)
172-
}
173-
} else {
174-
if fromChatEvent(&update, "commonlispbr") && !member.IsBot {
175-
username := getUserName(member)
176-
text := fmt.Sprintf(
177-
`Olá %s! Seja bem-vindo ao grupo oficial de Common Lisp do Brasil.
178-
Leia as regras em: https://lisp.com.br/rules.html.`,
179-
username,
180-
)
181-
reply(bot, &update, text)
182-
}
210+
kickTroll(bot, &update, member, trollHouse)
211+
} else if fromChatEvent(&update, "commonlispbr") && !member.IsBot {
212+
welcomeMessage(bot, &update, member)
183213
}
184214
}
185215
}

0 commit comments

Comments
 (0)