diff --git a/.gitignore b/.gitignore
index c419d97..53d5164 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-_data
\ No newline at end of file
+_data
+.idea
\ No newline at end of file
diff --git a/modules/macro/mod.go b/modules/macro/mod.go
index 043db14..0a7258b 100644
--- a/modules/macro/mod.go
+++ b/modules/macro/mod.go
@@ -50,49 +50,45 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
}
func (m *Module) OnUpdate(update tg.APIUpdate) {
- // Not a message? Ignore
- if update.Message == nil {
+ if !utils.IsCommand(update, m.name, "macro") {
return
}
- if utils.IsCommand(*update.Message, m.name, "macro") {
- parts := strings.SplitN(*(update.Message.Text), " ", 3)
- switch len(parts) {
- case 2:
- name := strings.ToLower(parts[1])
- item, ok := macros[name]
- var out string
- if ok {
- out = fmt.Sprintf("%s\n%s\n%s - %s", name, item.Value, item.Author.Username, item.Time.Format("02-01-06 15:04"))
- } else {
- out = fmt.Sprintf("%s\nmacro inesistente\n(configura con /macro %s contenuto)", name, name)
- }
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: update.Message.Chat.ChatID,
- Text: out,
- ReplyID: &update.Message.MessageID,
- })
- case 3:
- name := strings.ToLower(parts[1])
- macros[name] = Macro{
- Value: parts[2],
- Author: update.Message.User,
- Time: time.Now(),
- }
- m.save()
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: update.Message.Chat.ChatID,
- Text: fmt.Sprintf("%s → %s", name, parts[2]),
- ReplyID: &update.Message.MessageID,
- })
- default:
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: update.Message.Chat.ChatID,
- Text: "Sintassi\nLeggi: /macro nome-macro\nScrivi: /macro nome-macro contenuto macro",
- ReplyID: &update.Message.MessageID,
- })
+ parts := strings.SplitN(*(update.Message.Text), " ", 3)
+ switch len(parts) {
+ case 2:
+ name := strings.ToLower(parts[1])
+ item, ok := macros[name]
+ var out string
+ if ok {
+ out = fmt.Sprintf("%s\n%s\n%s - %s", name, item.Value, item.Author.Username, item.Time.Format("02-01-06 15:04"))
+ } else {
+ out = fmt.Sprintf("%s\nmacro inesistente\n(configura con /macro %s contenuto)", name, name)
}
- return
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: update.Message.Chat.ChatID,
+ Text: out,
+ ReplyID: &update.Message.MessageID,
+ })
+ case 3:
+ name := strings.ToLower(parts[1])
+ macros[name] = Macro{
+ Value: parts[2],
+ Author: update.Message.User,
+ Time: time.Now(),
+ }
+ m.save()
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: update.Message.Chat.ChatID,
+ Text: fmt.Sprintf("%s → %s", name, parts[2]),
+ ReplyID: &update.Message.MessageID,
+ })
+ default:
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: update.Message.Chat.ChatID,
+ Text: "Sintassi\nLeggi: /macro nome-macro\nScrivi: /macro nome-macro contenuto macro",
+ ReplyID: &update.Message.MessageID,
+ })
}
}
diff --git a/modules/metafora/mod.go b/modules/metafora/mod.go
index 20fa1aa..63ce6d7 100644
--- a/modules/metafora/mod.go
+++ b/modules/metafora/mod.go
@@ -44,18 +44,14 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
}
func (m *Module) OnUpdate(update tg.APIUpdate) {
- // Not a message? Ignore
- if update.Message == nil {
+ if !utils.IsCommand(update, m.name, "metafora") {
return
}
- if utils.IsCommand(*update.Message, m.name, "metafora") {
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: update.Message.Chat.ChatID,
- Text: metaforaAPI(),
- })
- return
- }
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: update.Message.Chat.ChatID,
+ Text: metaforaAPI(),
+ })
}
func metaforaAPI() string {
diff --git a/modules/proverbio/mod.go b/modules/proverbio/mod.go
index becc147..6bd0764 100644
--- a/modules/proverbio/mod.go
+++ b/modules/proverbio/mod.go
@@ -46,18 +46,14 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
}
func (m *Module) OnUpdate(update tg.APIUpdate) {
- // Not a message? Ignore
- if update.Message == nil {
+ if !utils.IsCommand(update, m.name, "proverbio") {
return
}
- if utils.IsCommand(*update.Message, m.name, "proverbio") {
- start := rand.Intn(len(proverbipairs.start))
- end := rand.Intn(len(proverbipairs.end))
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: update.Message.Chat.ChatID,
- Text: fmt.Sprintf("Dice il saggio:\n%s %s", proverbipairs.start[start], proverbipairs.end[end]),
- })
- return
- }
+ start := rand.Intn(len(proverbipairs.start))
+ end := rand.Intn(len(proverbipairs.end))
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: update.Message.Chat.ChatID,
+ Text: fmt.Sprintf("Dice il saggio:\n%s %s", proverbipairs.start[start], proverbipairs.end[end]),
+ })
}
diff --git a/modules/remind/cmd.remind.go b/modules/remind/cmd.remind.go
new file mode 100644
index 0000000..ce682b8
--- /dev/null
+++ b/modules/remind/cmd.remind.go
@@ -0,0 +1,102 @@
+package remind
+
+import (
+ "git.fromouter.space/hamcha/tg"
+ "strconv"
+ "strings"
+ "time"
+)
+
+func (m *Module) cmdRicordami(message *tg.APIMessage) {
+ // Supported formats:
+ // Xs/m/h/d => in X seconds/minutes/hours/days
+ // HH:MM => at HH:MM (24 hour format)
+ // HH:MM:SS => at HH:MM:SS (24 hour format)
+ // dd/mm/yyyy => same hour, specific date
+ // dd/mm/yyyy-HH:MM => specific hour, specific dat
+ // dd/mm/yyyy-HH:MM:SS => specific hour, specific date
+
+ parts := strings.SplitN(*message.Text, " ", 3)
+
+ // Little hack to allow text-less reminders with replies
+ if len(parts) == 2 && message.ReplyTo != nil {
+ parts = append(parts, "")
+ }
+
+ if len(parts) < 3 {
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "Sintassi\n/ricordami [quando] Messaggio\n\nFormati supportati per [quando]:\n 10s 10m 10h 10d (secondi/minuti/ore/giorni)\n 13:20 15:55:01 (ora dello stesso giorno, formato 24h)\n 11/02/2099 11/02/2099-11:20:01 (giorno diverso, stessa ora [1] o specifica [2])",
+ ReplyID: &message.MessageID,
+ })
+ return
+ }
+
+ format := parts[1]
+ remindText := parts[2]
+
+ loc := defaultLocation
+ /*TODO REDO
+ if uloc, ok := tzlocs[update.User.UserID]; ok {
+ loc = uloc
+ }
+ */
+ timestamp, err := parseDuration(format, loc)
+ if err != nil {
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: err.Error(),
+ ReplyID: &message.MessageID,
+ })
+ return
+ }
+
+ id := strconv.FormatInt(message.Chat.ChatID, 36) + "-" + strconv.FormatInt(message.MessageID, 36)
+ reminder := Reminder{
+ ReminderID: id,
+ TargetID: message.User.UserID,
+ When: timestamp.Unix(),
+ Text: remindText,
+ }
+ if message.ReplyTo != nil {
+ reminder.Reference = &ReminderReference{
+ Chat: message.Chat.ChatID,
+ Message: message.ReplyTo.MessageID,
+ }
+ }
+ pending.SetKey(id, reminder)
+ m.save()
+ go m.schedule(id)
+
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "Ok, vedrò di avvisarti " + formatWhen(timestamp, loc),
+ ReplyID: &message.MessageID,
+ })
+}
+
+func (m *Module) schedule(id string) {
+ // Get reminder
+ r := pending.GetKey(id)
+ remaining := r.When - time.Now().Unix()
+ if remaining > 0 {
+ // Wait remaining time
+ time.Sleep(time.Second * time.Duration(remaining))
+ }
+ // Remind!
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: r.TargetID,
+ Text: "Heyla! Mi avevi chiesto di ricordarti questo:\n" + r.Text,
+ ReplyID: nil,
+ })
+ if r.Reference != nil {
+ m.client.ForwardMessage(tg.ClientForwardMessageData{
+ ChatID: r.TargetID,
+ FromChatID: r.Reference.Chat,
+ MessageID: r.Reference.Message,
+ })
+ }
+ // Delete reminder from pending list and save list to disk
+ pending.DeleteKey(id)
+ m.save()
+}
diff --git a/modules/remind/cmd.reminders.go b/modules/remind/cmd.reminders.go
new file mode 100644
index 0000000..daa77c6
--- /dev/null
+++ b/modules/remind/cmd.reminders.go
@@ -0,0 +1,123 @@
+package remind
+
+import (
+ "fmt"
+ "git.fromouter.space/hamcha/tg"
+ "log"
+ "sort"
+ "time"
+)
+
+func (m *Module) cmdReminder(message *tg.APIMessage) {
+
+ // Should only work in private chats
+ if message.Chat.Type != tg.ChatTypePrivate {
+
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "Per favore chiedimi in privato dei reminder",
+ ReplyID: &message.MessageID,
+ })
+ return
+ }
+
+ reminders := remindersOfUser(message.User.UserID)
+
+ if len(reminders) == 0 {
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "Non ci sono reminder in coda per te",
+ ReplyID: &message.MessageID,
+ })
+ } else {
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: renderReminderEntry(0, reminders, defaultLocation),
+ ReplyID: &message.MessageID,
+ ReplyMarkup: &tg.APIInlineKeyboardMarkup{
+ InlineKeyboard: [][]tg.APIInlineKeyboardButton{makeReminderInlineKeyboard(0, reminders)},
+ },
+ })
+ }
+}
+
+func (m *Module) handleCallback(callback *tg.APICallbackQuery) {
+ if callback.Data == nil {
+ return
+ }
+
+ var command, reminderID string
+ _, err := fmt.Sscan(*callback.Data, &command, &reminderID)
+ if err != nil {
+ log.Println("[remind] WARN: weird callback received: " + err.Error())
+ return
+ }
+
+ reminders := remindersOfUser(callback.User.UserID)
+
+ switch command {
+ case "GET":
+ current := 0
+ for index, reminder := range reminders {
+ if reminder.ReminderID == reminderID {
+ current = index
+ break
+ }
+ }
+ // Modify message with requested reminder
+ m.client.EditText(tg.ClientEditTextData{
+ ChatID: callback.Message.Chat.ChatID,
+ MessageID: callback.Message.MessageID,
+ Text: renderReminderEntry(current, reminders, defaultLocation),
+ ReplyMarkup: &tg.APIInlineKeyboardMarkup{
+ InlineKeyboard: [][]tg.APIInlineKeyboardButton{makeReminderInlineKeyboard(current, reminders)},
+ },
+ })
+ }
+}
+
+func remindersOfUser(userID int64) (reminders []Reminder) {
+ for _, reminder := range pending.Copy() {
+ if reminder.TargetID == userID {
+ reminders = append(reminders, reminder)
+ }
+ }
+ if len(reminders) > 0 {
+ sort.Slice(reminders, func(a, b int) bool { return reminders[a].When < reminders[b].When })
+ }
+ return
+}
+
+func renderReminderEntry(current int, reminders []Reminder, loc *time.Location) string {
+ reminder := reminders[current]
+ when := time.Unix(reminder.When, 0)
+ message := reminder.Text
+ if reminder.Reference != nil {
+ if message != "" {
+ message += ", "
+ }
+ message += fmt.Sprintf("https://t.me/c/%d/%d", reminder.Reference.Chat, reminder.Reference.Message)
+ }
+
+ return fmt.Sprintf("Reminder %d (/%d):\n%s\nQuando: %s", current+1, len(reminders), message, formatWhen(when, loc))
+}
+
+func makeReminderInlineKeyboard(current int, reminders []Reminder) (buttons []tg.APIInlineKeyboardButton) {
+ if current > 0 {
+ buttons = append(buttons, tg.APIInlineKeyboardButton{
+ Text: "⬅",
+ CallbackData: fmt.Sprintf("GET %s", reminders[current-1].ReminderID),
+ })
+ }
+ buttons = append(buttons, tg.APIInlineKeyboardButton{
+ Text: "❌",
+ CallbackData: fmt.Sprintf("DELETE %s", reminders[current].ReminderID),
+ })
+ if current < len(reminders)-1 {
+ buttons = append(buttons, tg.APIInlineKeyboardButton{
+ Text: "➡",
+ CallbackData: fmt.Sprintf("GET %s", reminders[current+1].ReminderID),
+ })
+ }
+ return
+}
diff --git a/modules/remind/data.go b/modules/remind/data.go
new file mode 100644
index 0000000..db8c84e
--- /dev/null
+++ b/modules/remind/data.go
@@ -0,0 +1,19 @@
+package remind
+
+import (
+ "github.com/cockroachdb/pebble"
+ jsoniter "github.com/json-iterator/go"
+ "log"
+)
+
+func (m *Module) save() {
+ byt, err := jsoniter.ConfigFastest.Marshal(pending.Copy())
+ if err != nil {
+ log.Println("[remind] WARN: Could not encode reminders: " + err.Error())
+ }
+ err = m.kv.Set([]byte(reminderKey), byt, &pebble.WriteOptions{Sync: true})
+ if err != nil {
+ log.Println("[remind] WARN: Could not save reminders to db: " + err.Error())
+ return
+ }
+}
diff --git a/modules/remind/mod.go b/modules/remind/mod.go
index 10a0e87..4502d13 100644
--- a/modules/remind/mod.go
+++ b/modules/remind/mod.go
@@ -2,12 +2,8 @@ package remind
import (
"errors"
- "fmt"
"log"
- "strconv"
- "strings"
"time"
- "unicode"
"git.fromouter.space/crunchy-rocks/clessy-ng/modules"
"git.fromouter.space/crunchy-rocks/clessy-ng/utils"
@@ -15,14 +11,14 @@ import (
"git.fromouter.space/hamcha/tg"
"git.sr.ht/~hamcha/containers"
"github.com/cockroachdb/pebble"
- jsoniter "github.com/json-iterator/go"
)
type Reminder struct {
- TargetID int64
- When int64
- Text string
- Reference *ReminderReference
+ ReminderID string
+ TargetID int64
+ When int64
+ Text string
+ Reference *ReminderReference
}
type ReminderReference struct {
@@ -63,6 +59,20 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
return err
}
}
+
+ // Fix old reminders
+ dirty := false
+ for reminderID, reminder := range reminders {
+ if reminder.ReminderID == "" {
+ reminder.ReminderID = reminderID
+ dirty = true
+ }
+ }
+ if dirty {
+ m.save()
+ }
+
+ // Set schedule
for id, reminder := range reminders {
pending.SetKey(id, reminder)
go m.schedule(id)
@@ -73,240 +83,18 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
}
func (m *Module) OnUpdate(update tg.APIUpdate) {
- // Not a message? Ignore
- if update.Message == nil {
- return
+ // Check for updates to existing messages
+ if update.Callback != nil {
+ m.handleCallback(update.Callback)
}
- message := *update.Message
- if utils.IsCommand(message, m.name, "ricordami") {
- // Supported formats:
- // Xs/m/h/d => in X seconds/minutes/hours/days
- // HH:MM => at HH:MM (24 hour format)
- // HH:MM:SS => at HH:MM:SS (24 hour format)
- // dd/mm/yyyy => same hour, specific date
- // dd/mm/yyyy-HH:MM => specific hour, specific dat
- // dd/mm/yyyy-HH:MM:SS => specific hour, specific date
-
- parts := strings.SplitN(*message.Text, " ", 3)
-
- // Little hack to allow text-less reminders with replies
- if len(parts) == 2 && message.ReplyTo != nil {
- parts = append(parts, "")
- }
-
- if len(parts) < 3 {
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "Sintassi\n/ricordami [quando] Messaggio\n\nFormati supportati per [quando]:\n 10s 10m 10h 10d (secondi/minuti/ore/giorni)\n 13:20 15:55:01 (ora dello stesso giorno, formato 24h)\n 11/02/2099 11/02/2099-11:20:01 (giorno diverso, stessa ora [1] o specifica [2])",
- ReplyID: &message.MessageID,
- })
- return
- }
-
- format := parts[1]
- remindText := parts[2]
-
- loc := defaultLocation
- /*TODO REDO
- if uloc, ok := tzlocs[update.User.UserID]; ok {
- loc = uloc
- }
- */
- timestamp, err := parseDuration(format, loc)
- if err != nil {
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: err.Error(),
- ReplyID: &message.MessageID,
- })
- return
- }
-
- id := strconv.FormatInt(message.Chat.ChatID, 36) + "-" + strconv.FormatInt(message.MessageID, 36)
- reminder := Reminder{
- TargetID: message.User.UserID,
- When: timestamp.Unix(),
- Text: remindText,
- }
- if message.ReplyTo != nil {
- reminder.Reference = &ReminderReference{
- Chat: message.Chat.ChatID,
- Message: message.ReplyTo.MessageID,
- }
- }
- pending.SetKey(id, reminder)
- m.save()
- go m.schedule(id)
-
- whenday := "più tardi"
- _, todaym, todayd := time.Now().Date()
- _, targetm, targetd := timestamp.Date()
- if todaym != targetm || todayd != targetd {
- whenday = "il " + timestamp.In(loc).Format("2/1")
- }
- whentime := "alle " + timestamp.In(loc).Format("15:04:05")
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "Ok, vedrò di avvisarti " + whenday + " " + whentime,
- ReplyID: &message.MessageID,
- })
+ if utils.IsCommand(update, m.name, "ricordami") || utils.IsCommand(update, m.name, "remind") {
+ m.cmdRicordami(update.Message)
return
}
- if utils.IsCommand(message, m.name, "reminders") {
- // Should only work in private chats
- if message.Chat.Type != tg.ChatTypePrivate {
-
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "Per favore chiedimi in privato dei reminder",
- ReplyID: &message.MessageID,
- })
- return
- }
-
- useritems := []Reminder{}
- for _, reminder := range pending.Copy() {
- if reminder.TargetID == message.User.UserID {
- useritems = append(useritems, reminder)
- }
- }
-
- if len(useritems) == 0 {
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "Non ci sono reminder in coda per te",
- ReplyID: &message.MessageID,
- })
- } else {
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: fmt.Sprintf("Ci sono %d reminder in coda per te", len(useritems)),
- ReplyID: &message.MessageID,
- })
- }
- }
-}
-
-func (m *Module) schedule(id string) {
- // Get reminder
- r := pending.GetKey(id)
- remaining := r.When - time.Now().Unix()
- if remaining > 0 {
- // Wait remaining time
- time.Sleep(time.Second * time.Duration(remaining))
- }
- // Remind!
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: r.TargetID,
- Text: "Heyla! Mi avevi chiesto di ricordarti questo:\n" + r.Text,
- ReplyID: nil,
- })
- if r.Reference != nil {
- m.client.ForwardMessage(tg.ClientForwardMessageData{
- ChatID: r.TargetID,
- FromChatID: r.Reference.Chat,
- MessageID: r.Reference.Message,
- })
- }
- // Delete reminder from pending list and save list to disk
- pending.DeleteKey(id)
- m.save()
-}
-
-func (m *Module) save() {
- byt, err := jsoniter.ConfigFastest.Marshal(pending.Copy())
- if err != nil {
- log.Println("[remind] WARN: Could not encode reminders: " + err.Error())
- }
- err = m.kv.Set([]byte(reminderKey), byt, &pebble.WriteOptions{Sync: true})
- if err != nil {
- log.Println("[remind] WARN: Could not save reminders to db: " + err.Error())
+ if utils.IsCommand(update, m.name, "reminders") {
+ m.cmdReminder(update.Message)
return
}
}
-
-func isSscanfValid(n int, err error) bool {
- return err == nil
-}
-
-func scanMixedDelay(str string) (bool, time.Time, error) {
- remaining := str
- now := time.Now()
- num := 0
- sep := ' '
- for len(remaining) > 1 {
- _, err := fmt.Sscanf(remaining, "%d%c", &num, &sep)
- if err != nil {
- return false, now, err
- }
- dur := time.Duration(num)
- switch unicode.ToLower(sep) {
- case 's':
- dur *= time.Second
- case 'm':
- dur *= time.Minute
- case 'h':
- dur *= time.Hour
- case 'd':
- dur *= time.Hour * 24
- default:
- return true, now, fmt.Errorf("La durata ha una unità che non conosco, usa una di queste: s (secondi) m (minuti) h (ore) d (giorni)")
- }
- now = now.Add(dur)
- nextIndex := strings.IndexRune(remaining, sep)
- remaining = remaining[nextIndex+1:]
- }
- fmt.Printf("tot: %s", now.Sub(time.Now()))
- return true, now, nil
-}
-
-func parseDuration(date string, loc *time.Location) (time.Time, error) {
- now := time.Now().In(loc)
- hour := now.Hour()
- min := now.Minute()
- sec := now.Second()
- day := now.Day()
- month := now.Month()
- year := now.Year()
- dayunspecified := false
- isDurationFmt, duration, err := scanMixedDelay(date)
- switch {
- case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d-%d:%d:%d", &day, &month, &year, &hour, &min, &sec)):
- case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d-%d:%d", &day, &month, &year, &hour, &min)):
- sec = 0
- case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d", &day, &month, &year)):
- hour = now.Hour()
- min = now.Minute()
- sec = now.Second()
- case isSscanfValid(fmt.Sscanf(date, "%d:%d:%d", &hour, &min, &sec)):
- day = now.Day()
- month = now.Month()
- year = now.Year()
- dayunspecified = true
- case isSscanfValid(fmt.Sscanf(date, "%d:%d", &hour, &min)):
- day = now.Day()
- month = now.Month()
- year = now.Year()
- sec = 0
- dayunspecified = true
- case isDurationFmt:
- return duration, err
- default:
- return now, fmt.Errorf("Non capisco quando dovrei ricordartelo!")
- }
- targetDate := time.Date(year, month, day, hour, min, sec, 0, loc)
- if targetDate.Before(now) {
- // If day was not specified assume tomorrow
- if dayunspecified {
- targetDate = targetDate.Add(time.Hour * 24)
- } else {
- return now, fmt.Errorf("Non posso ricordarti cose nel passato!")
- }
- }
- if targetDate.After(now.Add(reminderMaxDuration)) {
- return now, fmt.Errorf("Non credo riuscirei a ricordarmi qualcosa per così tanto")
- }
- return targetDate, nil
-}
diff --git a/modules/remind/utils.go b/modules/remind/utils.go
new file mode 100644
index 0000000..467082e
--- /dev/null
+++ b/modules/remind/utils.go
@@ -0,0 +1,103 @@
+package remind
+
+import (
+ "fmt"
+ "strings"
+ "time"
+ "unicode"
+)
+
+func formatWhen(timestamp time.Time, loc *time.Location) string {
+ day := "più tardi"
+ _, todaym, todayd := time.Now().Date()
+ _, targetm, targetd := timestamp.Date()
+ if todaym != targetm || todayd != targetd {
+ day = "il " + timestamp.In(loc).Format("2/1")
+ }
+
+ return fmt.Sprintf("%s alle %s", day, timestamp.In(loc).Format("15:04:05"))
+}
+
+func isSscanfValid(n int, err error) bool {
+ return err == nil
+}
+
+func scanMixedDelay(str string) (bool, time.Time, error) {
+ remaining := str
+ now := time.Now()
+ num := 0
+ sep := ' '
+ for len(remaining) > 1 {
+ _, err := fmt.Sscanf(remaining, "%d%c", &num, &sep)
+ if err != nil {
+ return false, now, err
+ }
+ dur := time.Duration(num)
+ switch unicode.ToLower(sep) {
+ case 's':
+ dur *= time.Second
+ case 'm':
+ dur *= time.Minute
+ case 'h':
+ dur *= time.Hour
+ case 'd':
+ dur *= time.Hour * 24
+ default:
+ return true, now, fmt.Errorf("La durata ha una unità che non conosco, usa una di queste: s (secondi) m (minuti) h (ore) d (giorni)")
+ }
+ now = now.Add(dur)
+ nextIndex := strings.IndexRune(remaining, sep)
+ remaining = remaining[nextIndex+1:]
+ }
+ fmt.Printf("tot: %s", now.Sub(time.Now()))
+ return true, now, nil
+}
+
+func parseDuration(date string, loc *time.Location) (time.Time, error) {
+ now := time.Now().In(loc)
+ hour := now.Hour()
+ min := now.Minute()
+ sec := now.Second()
+ day := now.Day()
+ month := now.Month()
+ year := now.Year()
+ dayunspecified := false
+ isDurationFmt, duration, err := scanMixedDelay(date)
+ switch {
+ case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d-%d:%d:%d", &day, &month, &year, &hour, &min, &sec)):
+ case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d-%d:%d", &day, &month, &year, &hour, &min)):
+ sec = 0
+ case isSscanfValid(fmt.Sscanf(date, "%d/%d/%d", &day, &month, &year)):
+ hour = now.Hour()
+ min = now.Minute()
+ sec = now.Second()
+ case isSscanfValid(fmt.Sscanf(date, "%d:%d:%d", &hour, &min, &sec)):
+ day = now.Day()
+ month = now.Month()
+ year = now.Year()
+ dayunspecified = true
+ case isSscanfValid(fmt.Sscanf(date, "%d:%d", &hour, &min)):
+ day = now.Day()
+ month = now.Month()
+ year = now.Year()
+ sec = 0
+ dayunspecified = true
+ case isDurationFmt:
+ return duration, err
+ default:
+ return now, fmt.Errorf("Non capisco quando dovrei ricordartelo!")
+ }
+ targetDate := time.Date(year, month, day, hour, min, sec, 0, loc)
+ if targetDate.Before(now) {
+ // If day was not specified assume tomorrow
+ if dayunspecified {
+ targetDate = targetDate.Add(time.Hour * 24)
+ } else {
+ return now, fmt.Errorf("Non posso ricordarti cose nel passato!")
+ }
+ }
+ if targetDate.After(now.Add(reminderMaxDuration)) {
+ return now, fmt.Errorf("Non credo riuscirei a ricordarmi qualcosa per così tanto")
+ }
+ return targetDate, nil
+}
diff --git a/modules/unsplash/mod.go b/modules/unsplash/mod.go
index 9d64c81..b2b392f 100644
--- a/modules/unsplash/mod.go
+++ b/modules/unsplash/mod.go
@@ -73,53 +73,21 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
}
func (m *Module) OnUpdate(update tg.APIUpdate) {
- // Not a message? Ignore
- if update.Message == nil {
+ if !utils.IsCommand(update, m.name, "unsplash") {
return
}
message := *update.Message
- if utils.IsCommand(message, m.name, "unsplash") {
- text := ""
- user := message.User
+ text := ""
+ user := message.User
- if message.ReplyTo != nil {
- switch {
- case message.ReplyTo.Text != nil:
- text = *(message.ReplyTo.Text)
- case message.ReplyTo.Caption != nil:
- text = *(message.ReplyTo.Caption)
- default:
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "Non c'e' niente di 'ispiratore' in questo..",
- ReplyID: &message.MessageID,
- })
- return
- }
-
- // For forwarded message take the original user
- if message.FwdUser != nil {
- user = *message.FwdUser
- } else {
- user = message.ReplyTo.User
- }
- } else {
- if strings.Index(*(message.Text), " ") > 0 {
- text = strings.TrimSpace(strings.SplitN(*(message.Text), " ", 2)[1])
- }
- }
-
- // Cleanup chars
- text = strings.Map(stripUnreadable, text)
-
- author := user.FirstName
- if user.LastName != "" {
- author = user.FirstName + " " + user.LastName
- }
- author += " (" + user.Username + ")"
-
- if strings.TrimSpace(text) == "" {
+ if message.ReplyTo != nil {
+ switch {
+ case message.ReplyTo.Text != nil:
+ text = *(message.ReplyTo.Text)
+ case message.ReplyTo.Caption != nil:
+ text = *(message.ReplyTo.Caption)
+ default:
m.client.SendTextMessage(tg.ClientTextMessageData{
ChatID: message.Chat.ChatID,
Text: "Non c'e' niente di 'ispiratore' in questo..",
@@ -128,137 +96,166 @@ func (m *Module) OnUpdate(update tg.APIUpdate) {
return
}
- file, err := os.Open(pics[rand.Intn(len(pics))])
- if err != nil {
- log.Printf("[unsplash] Could not open original image file: %s\n", err.Error())
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "ERRORE! @hamcha controlla la console!",
- ReplyID: &message.MessageID,
- })
- return
+ // For forwarded message take the original user
+ if message.FwdUser != nil {
+ user = *message.FwdUser
+ } else {
+ user = message.ReplyTo.User
}
- defer file.Close()
-
- img, _, err := image.Decode(file)
- if err != nil {
- log.Printf("[unsplash] Image decode error: %s\n", err.Error())
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "ERRORE! @hamcha controlla la console!",
- ReplyID: &message.MessageID,
- })
- return
+ } else {
+ if strings.Index(*(message.Text), " ") > 0 {
+ text = strings.TrimSpace(strings.SplitN(*(message.Text), " ", 2)[1])
}
+ }
- m.client.SendChatAction(tg.ClientChatActionData{
- ChatID: message.Chat.ChatID,
- Action: tg.ActionUploadingPhoto,
+ // Cleanup chars
+ text = strings.Map(stripUnreadable, text)
+
+ author := user.FirstName
+ if user.LastName != "" {
+ author = user.FirstName + " " + user.LastName
+ }
+ author += " (" + user.Username + ")"
+
+ if strings.TrimSpace(text) == "" {
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "Non c'e' niente di 'ispiratore' in questo..",
+ ReplyID: &message.MessageID,
})
+ return
+ }
- // Darken image
- img = imaging.AdjustBrightness(imaging.AdjustGamma(imaging.AdjustSigmoid(img, 0.5, -6.0), 0.8), -20)
+ file, err := os.Open(pics[rand.Intn(len(pics))])
+ if err != nil {
+ log.Printf("[unsplash] Could not open original image file: %s\n", err.Error())
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "ERRORE! @hamcha controlla la console!",
+ ReplyID: &message.MessageID,
+ })
+ return
+ }
+ defer file.Close()
- // Create target image
- bounds := img.Bounds()
- iwidth := float64(bounds.Size().X)
- iheight := float64(bounds.Size().Y)
+ img, _, err := image.Decode(file)
+ if err != nil {
+ log.Printf("[unsplash] Image decode error: %s\n", err.Error())
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "ERRORE! @hamcha controlla la console!",
+ ReplyID: &message.MessageID,
+ })
+ return
+ }
- timg := image.NewRGBA(bounds)
- gc := draw2dimg.NewGraphicContext(timg)
- gc.Emojis = m.emojis
- gc.SetFontData(quoteFontData)
- gc.DrawImage(img)
- gc.SetStrokeColor(image.Black)
- gc.SetFillColor(image.White)
+ m.client.SendChatAction(tg.ClientChatActionData{
+ ChatID: message.Chat.ChatID,
+ Action: tg.ActionUploadingPhoto,
+ })
- text = strings.ToUpper(strings.TrimSpace(text))
- gc.Restore()
- gc.Save()
+ // Darken image
+ img = imaging.AdjustBrightness(imaging.AdjustGamma(imaging.AdjustSigmoid(img, 0.5, -6.0), 0.8), -20)
- // Detect appropriate font size
- scale := iheight / iwidth * (iwidth / 10) * 0.8
- gc.SetFontSize(scale)
- gc.SetLineWidth(scale / 15)
+ // Create target image
+ bounds := img.Bounds()
+ iwidth := float64(bounds.Size().X)
+ iheight := float64(bounds.Size().Y)
- // Get NEW bounds
- left, top, right, bottom := gc.GetStringBounds(text)
+ timg := image.NewRGBA(bounds)
+ gc := draw2dimg.NewGraphicContext(timg)
+ gc.Emojis = m.emojis
+ gc.SetFontData(quoteFontData)
+ gc.DrawImage(img)
+ gc.SetStrokeColor(image.Black)
+ gc.SetFillColor(image.White)
- width := right - left
- texts := []string{text}
- if width*1.2 > iwidth {
- // Split text
- texts = utils.SplitCenter(text)
+ text = strings.ToUpper(strings.TrimSpace(text))
+ gc.Restore()
+ gc.Save()
- // Get longest line
- longer := float64(0)
- longid := 0
- widths := make([]float64, len(texts))
- for id := range texts {
- tleft, _, tright, _ := gc.GetStringBounds(texts[id])
- widths[id] = tright - tleft
- if width > longer {
- longer = widths[id]
- longid = id
- }
- }
+ // Detect appropriate font size
+ scale := iheight / iwidth * (iwidth / 10) * 0.8
+ gc.SetFontSize(scale)
+ gc.SetLineWidth(scale / 15)
- // Still too big? Decrease font size again
- iter := 0
- for width*1.2 > iwidth && iter < 10 {
- scale *= (0.9 - 0.05*float64(iter))
- gc.SetFontSize(scale)
- left, top, right, bottom = gc.GetStringBounds(texts[longid])
- width = right - left
- iter++
+ // Get NEW bounds
+ left, top, right, bottom := gc.GetStringBounds(text)
+
+ width := right - left
+ texts := []string{text}
+ if width*1.2 > iwidth {
+ // Split text
+ texts = utils.SplitCenter(text)
+
+ // Get longest line
+ longer := float64(0)
+ longid := 0
+ widths := make([]float64, len(texts))
+ for id := range texts {
+ tleft, _, tright, _ := gc.GetStringBounds(texts[id])
+ widths[id] = tright - tleft
+ if width > longer {
+ longer = widths[id]
+ longid = id
}
}
- texts = append(texts, author)
- height := bottom - top + 20
- margin := float64(height / 50)
- txtheight := (height + margin) * float64(len(texts))
+ // Still too big? Decrease font size again
+ iter := 0
+ for width*1.2 > iwidth && iter < 10 {
+ scale *= (0.9 - 0.05*float64(iter))
+ gc.SetFontSize(scale)
+ left, top, right, bottom = gc.GetStringBounds(texts[longid])
+ width = right - left
+ iter++
+ }
+ }
+ texts = append(texts, author)
+ height := bottom - top + 20
+ margin := float64(height / 50)
+ txtheight := (height + margin) * float64(len(texts))
+
+ gc.Save()
+ for id, txt := range texts {
gc.Save()
- for id, txt := range texts {
- gc.Save()
+ left, _, right, _ = gc.GetStringBounds(txt)
+ width = right - left
+
+ x := (iwidth - width) / 2
+ y := (iheight-txtheight)/2 + (height+margin*2)*float64(id+1)
+ if id == len(texts)-1 {
+ gc.SetFontSize(scale * 0.7)
left, _, right, _ = gc.GetStringBounds(txt)
width = right - left
-
- x := (iwidth - width) / 2
- y := (iheight-txtheight)/2 + (height+margin*2)*float64(id+1)
- if id == len(texts)-1 {
- gc.SetFontSize(scale * 0.7)
- left, _, right, _ = gc.GetStringBounds(txt)
- width = right - left
- x = (iwidth - width) / 1.5
- y = (iheight-txtheight)/2 + (height+margin)*float64(id+1) + margin*6
- }
-
- gc.Translate(x, y)
- gc.StrokeString(txt)
- gc.FillString(txt)
- gc.Restore()
+ x = (iwidth - width) / 1.5
+ y = (iheight-txtheight)/2 + (height+margin)*float64(id+1) + margin*6
}
- buf := new(bytes.Buffer)
- err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80}))
- if err != nil {
- log.Printf("[unsplash] Image encode error: %s\n", err.Error())
- m.client.SendTextMessage(tg.ClientTextMessageData{
- ChatID: message.Chat.ChatID,
- Text: "ERRORE! @hamcha controlla la console!",
- ReplyID: &message.MessageID,
- })
- return
- }
- m.client.SendPhoto(tg.ClientPhotoData{
- ChatID: message.Chat.ChatID,
- Bytes: buf.Bytes(),
- Filename: "quote.jpg",
- ReplyID: &message.MessageID,
- })
+ gc.Translate(x, y)
+ gc.StrokeString(txt)
+ gc.FillString(txt)
+ gc.Restore()
}
+
+ buf := new(bytes.Buffer)
+ err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80}))
+ if err != nil {
+ log.Printf("[unsplash] Image encode error: %s\n", err.Error())
+ m.client.SendTextMessage(tg.ClientTextMessageData{
+ ChatID: message.Chat.ChatID,
+ Text: "ERRORE! @hamcha controlla la console!",
+ ReplyID: &message.MessageID,
+ })
+ return
+ }
+ m.client.SendPhoto(tg.ClientPhotoData{
+ ChatID: message.Chat.ChatID,
+ Bytes: buf.Bytes(),
+ Filename: "quote.jpg",
+ ReplyID: &message.MessageID,
+ })
}
func stripUnreadable(r rune) rune {
diff --git a/utils/command.go b/utils/command.go
index 1c18521..87fc91f 100644
--- a/utils/command.go
+++ b/utils/command.go
@@ -6,12 +6,17 @@ import (
"git.fromouter.space/hamcha/tg"
)
-func IsCommand(update tg.APIMessage, botname string, cmdname string) bool {
- if update.Text == nil {
+func IsCommand(update tg.APIUpdate, botname string, cmdname string) bool {
+ if update.Message == nil {
+ return false
+ }
+ message := update.Message
+
+ if message.Text == nil {
return false
}
- text := strings.TrimSpace(*(update.Text))
+ text := strings.TrimSpace(*(message.Text))
shortcmd := "/" + cmdname
fullcmd := shortcmd + "@" + botname