Skip to content

Commit

Permalink
feat: reminder (#50)
Browse files Browse the repository at this point in the history
* feat(reminder): wip lexer

* feat(reminder): proper lexer function

* feat(reminder): handler

* feat(reminder): proper handler

* test(reminder): set global timezone to UTC

* feat: register reminder on main program
  • Loading branch information
aldy505 authored Dec 26, 2023
1 parent 6cb9adf commit c603e00
Show file tree
Hide file tree
Showing 28 changed files with 972 additions and 38 deletions.
12 changes: 3 additions & 9 deletions captcha/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import (
"context"
"encoding/json"
"errors"
"github.com/getsentry/sentry-go"
"strconv"
"strings"
"time"

"github.com/getsentry/sentry-go"

"github.com/teknologi-umum/captcha/shared"
"github.com/teknologi-umum/captcha/utils"

Expand Down Expand Up @@ -129,7 +130,7 @@ func (d *Dependencies) CaptchaUserJoin(ctx context.Context, m *tb.Message) {
strings.Replace(DefaultQuestion, "{captcha}", captcha, 1),
"{user}",
"<a href=\"tg://user?id="+strconv.FormatInt(m.Sender.ID, 10)+"\">"+
sanitizeInput(m.Sender.FirstName)+utils.ShouldAddSpace(m.Sender)+sanitizeInput(m.Sender.LastName)+
utils.SanitizeInput(m.Sender.FirstName)+utils.ShouldAddSpace(m.Sender)+utils.SanitizeInput(m.Sender.LastName)+
"</a>",
1,
)
Expand Down Expand Up @@ -220,10 +221,3 @@ SENDMSG_RETRY:

d.waitOrDelete(ctx, m)
}

func sanitizeInput(inp string) string {
var str string
str = strings.ReplaceAll(inp, ">", "&gt;")
str = strings.ReplaceAll(str, "<", "&lt;")
return str
}
3 changes: 2 additions & 1 deletion captcha/leave.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package captcha
import (
"context"
"encoding/json"
"github.com/getsentry/sentry-go"
"strconv"

"github.com/getsentry/sentry-go"

"github.com/teknologi-umum/captcha/shared"
"github.com/teknologi-umum/captcha/utils"

Expand Down
4 changes: 2 additions & 2 deletions captcha/non.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ func (d *Dependencies) NonTextListener(ctx context.Context, m *tb.Message) {
wrongMsg, err := d.Bot.Send(
m.Chat,
"Hai, <a href=\"tg://user?id="+strconv.FormatInt(m.Sender.ID, 10)+"\">"+
sanitizeInput(m.Sender.FirstName)+
utils.SanitizeInput(m.Sender.FirstName)+
utils.ShouldAddSpace(m.Sender)+
sanitizeInput(m.Sender.LastName)+
utils.SanitizeInput(m.Sender.LastName)+
"</a>. "+
"Selesain captchanya dulu yuk, baru kirim yang aneh-aneh. Kamu punya "+
strconv.Itoa(int(remainingTime.Seconds()))+
Expand Down
4 changes: 2 additions & 2 deletions captcha/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ func (d *Dependencies) waitOrDelete(ctx context.Context, msgUser *tb.Message) {
kickMsg, err := d.Bot.Send(
msgUser.Chat,
"<a href=\"tg://user?id="+strconv.FormatInt(msgUser.Sender.ID, 10)+"\">"+
sanitizeInput(msgUser.Sender.FirstName)+
utils.SanitizeInput(msgUser.Sender.FirstName)+
utils.ShouldAddSpace(msgUser.Sender)+
sanitizeInput(msgUser.Sender.LastName)+
utils.SanitizeInput(msgUser.Sender.LastName)+
"</a> nggak nyelesain captcha, mari kita kick!",
&tb.SendOptions{
ParseMode: tb.ModeHTML,
Expand Down
4 changes: 2 additions & 2 deletions captcha/welcome.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ func (d *Dependencies) sendWelcomeMessage(ctx context.Context, m *tb.Message) er
strings.NewReplacer(
"{user}",
"<a href=\"tg://user?id="+strconv.FormatInt(m.Sender.ID, 10)+"\">"+
sanitizeInput(m.Sender.FirstName)+utils.ShouldAddSpace(m.Sender)+sanitizeInput(m.Sender.LastName)+
utils.SanitizeInput(m.Sender.FirstName)+utils.ShouldAddSpace(m.Sender)+utils.SanitizeInput(m.Sender.LastName)+
"</a>",
"{groupname}",
sanitizeInput(m.Chat.Title),
utils.SanitizeInput(m.Chat.Title),
).Replace(msgToSend),
&tb.SendOptions{
ReplyTo: m,
Expand Down
20 changes: 19 additions & 1 deletion cmd/captcha/captcha.go → cmd/captcha/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package main
import (
"context"
"fmt"
"github.com/teknologi-umum/captcha/setir"
"strconv"
"strings"
"time"

"github.com/teknologi-umum/captcha/reminder"
"github.com/teknologi-umum/captcha/setir"

"github.com/getsentry/sentry-go"
"github.com/teknologi-umum/captcha/analytics"
"github.com/teknologi-umum/captcha/ascii"
Expand All @@ -31,6 +33,7 @@ type Dependency struct {
Badwords *badwords.Dependency
UnderAttack *underattack.Dependency
Setir *setir.Dependency
Reminder *reminder.Dependency
}

// New returns a pointer struct of Dependency
Expand All @@ -54,6 +57,10 @@ func New(deps Dependency) (*Dependency, error) {
return nil, fmt.Errorf("badwords insertion feature is enabled, but badwords dependency is nil")
}

if deps.FeatureFlag.Reminder && deps.Reminder == nil {
return nil, fmt.Errorf("reminder feature is enabled, but reminder dependency is nil")
}

return &deps, nil
}

Expand Down Expand Up @@ -202,6 +209,17 @@ func (d *Dependency) DisableUnderAttackModeHandler(c tb.Context) error {
return d.UnderAttack.DisableUnderAttackModeHandler(ctx, c)
}

func (d *Dependency) ReminderHandler(c tb.Context) error {
ctx := sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone())

span := sentry.StartSpan(ctx, "bot.reminder_handler", sentry.WithTransactionSource(sentry.SourceTask),
sentry.WithTransactionName("Captcha ReminderHandler"))
defer span.Finish()
ctx = span.Context()

return d.Reminder.Handler(ctx, c)
}

func (d *Dependency) SetirHandler(c tb.Context) error {
ctx := sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone())

Expand Down
1 change: 1 addition & 0 deletions cmd/captcha/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type FeatureFlag struct {
BadwordsInsertion bool `yaml:"badwords_insertion" json:"badwords_insertion" env:"FEATURE_FLAG_BADWORDS_INSERTION"`
Dukun bool `yaml:"dukun" json:"dukun" env:"FEATURE_FLAG_DUKUN"`
UnderAttack bool `yaml:"under_attack" json:"under_attack" env:"FEATURE_FLAG_UNDER_ATTACK" env-default:"true"`
Reminder bool `yaml:"reminder" json:"reminder" env:"FEATURE_FLAG_REMINDER"`
}

type Configuration struct {
Expand Down
29 changes: 22 additions & 7 deletions cmd/captcha/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ package main
import (
"context"
"fmt"
"github.com/teknologi-umum/captcha/ascii"
"github.com/teknologi-umum/captcha/badwords"
"github.com/teknologi-umum/captcha/captcha"
"github.com/teknologi-umum/captcha/setir"
"github.com/teknologi-umum/captcha/underattack"
"github.com/teknologi-umum/captcha/underattack/datastore"
"log"
"net"
"net/http"
Expand All @@ -32,10 +26,17 @@ import (
"strings"
"time"

// Internals
"github.com/teknologi-umum/captcha/analytics"
"github.com/teknologi-umum/captcha/analytics/server"
"github.com/teknologi-umum/captcha/ascii"
"github.com/teknologi-umum/captcha/badwords"
"github.com/teknologi-umum/captcha/captcha"
"github.com/teknologi-umum/captcha/reminder"
"github.com/teknologi-umum/captcha/setir"
"github.com/teknologi-umum/captcha/shared"
"github.com/teknologi-umum/captcha/underattack"
"github.com/teknologi-umum/captcha/underattack/datastore"

// Database and cache
"github.com/allegro/bigcache/v3"
"github.com/jmoiron/sqlx"
Expand Down Expand Up @@ -279,6 +280,16 @@ func main() {
return
}

var reminderDependency *reminder.Dependency
if configuration.FeatureFlag.Reminder {
reminderDependency, err = reminder.New(cache)
if err != nil {
sentry.CaptureException(err)
log.Fatal(err)
return
}
}

program, err := New(Dependency{
FeatureFlag: configuration.FeatureFlag,
Captcha: &captcha.Dependencies{
Expand All @@ -291,6 +302,7 @@ func main() {
Badwords: badwordsDependency,
UnderAttack: underAttackDependency,
Setir: setirDependency,
Reminder: reminderDependency,
})
if err != nil {
sentry.CaptureException(err)
Expand Down Expand Up @@ -335,6 +347,9 @@ func main() {
b.Handle("/underattack", program.EnableUnderAttackModeHandler)
b.Handle("/disableunderattack", program.DisableUnderAttackModeHandler)

// Reminder (temporary feature)
b.Handle("/remind", program.ReminderHandler)

// Bad word handlers
b.Handle("/badwords", program.BadWordHandler)

Expand Down
65 changes: 65 additions & 0 deletions reminder/clock_parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package reminder

import (
"bufio"
"errors"
"fmt"
"strconv"
"strings"
)

var ErrParseClock = errors.New("parse clock")

func ParseClock(s string) (hour int, minute int, err error) {
scanner := bufio.NewScanner(strings.NewReader(s))
scanner.Split(bufio.ScanRunes)

var colonMark bool
var s_hour string
var s_minute string
for scanner.Scan() {
t := scanner.Text()
if t == ":" {
colonMark = true
continue
}

if !colonMark {
s_hour += t
} else {
s_minute += t
}
}

hour, err = strconv.Atoi(s_hour)
if err != nil {
return
}

minute, err = strconv.Atoi(s_minute)
if err != nil {
return
}

if hour >= 24 {
err = fmt.Errorf("%w: invalid hour, exceeds 24", ErrParseClock)
return
}

if hour < 0 {
err = fmt.Errorf("%w: invalid hour, negative number", ErrParseClock)
return
}

if minute >= 60 {
err = fmt.Errorf("%w: invalid minute, exceeds 60", ErrParseClock)
return
}

if minute < 0 {
err = fmt.Errorf("%w: invalid minute, negative number", ErrParseClock)
return
}

return
}
107 changes: 107 additions & 0 deletions reminder/clock_parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package reminder_test

import (
"errors"
"strconv"
"testing"

"github.com/teknologi-umum/captcha/reminder"
)

func TestParseClock(t *testing.T) {
testCases := []struct {
name string
input string
expectHour int
expectMinute int
expectError error
}{
{
name: "happy case 1",
input: "00:00",
expectHour: 0,
expectMinute: 0,
expectError: nil,
},
{
name: "happy case 2",
input: "23:59",
expectHour: 23,
expectMinute: 59,
expectError: nil,
},
{
name: "happy case 3",
input: "05:05",
expectHour: 5,
expectMinute: 5,
expectError: nil,
},
{
name: "happy case 4",
input: "20:20",
expectHour: 20,
expectMinute: 20,
expectError: nil,
},
{
name: "hour is not a number",
input: "abc:00",
expectHour: 0,
expectMinute: 0,
expectError: strconv.ErrSyntax,
},
{
name: "minute is not a number",
input: "15:abc",
expectHour: 15,
expectMinute: 0,
expectError: strconv.ErrSyntax,
},
{
name: "hour exceeds 24",
input: "30:20",
expectHour: 30,
expectMinute: 20,
expectError: reminder.ErrParseClock,
},
{
name: "hour negative number",
input: "-12:20",
expectHour: -12,
expectMinute: 20,
expectError: reminder.ErrParseClock,
},
{
name: "minute exceeds 60",
input: "20:90",
expectHour: 20,
expectMinute: 90,
expectError: reminder.ErrParseClock,
},
{
name: "minute negative number",
input: "00:-123",
expectHour: 0,
expectMinute: -123,
expectError: reminder.ErrParseClock,
},
}

for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
hour, minute, err := reminder.ParseClock(testCase.input)
if hour != testCase.expectHour {
t.Errorf("expecting hour to be %d, got %d", testCase.expectHour, hour)
}

if minute != testCase.expectMinute {
t.Errorf("expecting minute to be %d, got %d", testCase.expectMinute, minute)
}

if !errors.Is(err, testCase.expectError) {
t.Errorf("expecting error to be %v, got %v", testCase.expectError, err)
}
})
}
}
Loading

0 comments on commit c603e00

Please sign in to comment.