Refactor reminders into a more powerful package
This commit is contained in:
parent
3f1f822b8c
commit
417f9f9345
11 changed files with 577 additions and 451 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
_data
|
_data
|
||||||
|
.idea
|
|
@ -50,49 +50,45 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
||||||
// Not a message? Ignore
|
if !utils.IsCommand(update, m.name, "macro") {
|
||||||
if update.Message == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.IsCommand(*update.Message, m.name, "macro") {
|
parts := strings.SplitN(*(update.Message.Text), " ", 3)
|
||||||
parts := strings.SplitN(*(update.Message.Text), " ", 3)
|
switch len(parts) {
|
||||||
switch len(parts) {
|
case 2:
|
||||||
case 2:
|
name := strings.ToLower(parts[1])
|
||||||
name := strings.ToLower(parts[1])
|
item, ok := macros[name]
|
||||||
item, ok := macros[name]
|
var out string
|
||||||
var out string
|
if ok {
|
||||||
if ok {
|
out = fmt.Sprintf("<b>%s</b>\n%s\n<i>%s - %s</i>", name, item.Value, item.Author.Username, item.Time.Format("02-01-06 15:04"))
|
||||||
out = fmt.Sprintf("<b>%s</b>\n%s\n<i>%s - %s</i>", name, item.Value, item.Author.Username, item.Time.Format("02-01-06 15:04"))
|
} else {
|
||||||
} else {
|
out = fmt.Sprintf("<b>%s</b>\n<i>macro inesistente</i>\n(configura con /macro %s <i>contenuto</i>)", name, name)
|
||||||
out = fmt.Sprintf("<b>%s</b>\n<i>macro inesistente</i>\n(configura con /macro %s <i>contenuto</i>)", 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("<b>%s</b> → %s", name, parts[2]),
|
|
||||||
ReplyID: &update.Message.MessageID,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
m.client.SendTextMessage(tg.ClientTextMessageData{
|
|
||||||
ChatID: update.Message.Chat.ChatID,
|
|
||||||
Text: "<b>Sintassi</b>\n<b>Leggi</b>: /macro <i>nome-macro</i>\n<b>Scrivi</b>: /macro <i>nome-macro</i> <i>contenuto macro</i>",
|
|
||||||
ReplyID: &update.Message.MessageID,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
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("<b>%s</b> → %s", name, parts[2]),
|
||||||
|
ReplyID: &update.Message.MessageID,
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
m.client.SendTextMessage(tg.ClientTextMessageData{
|
||||||
|
ChatID: update.Message.Chat.ChatID,
|
||||||
|
Text: "<b>Sintassi</b>\n<b>Leggi</b>: /macro <i>nome-macro</i>\n<b>Scrivi</b>: /macro <i>nome-macro</i> <i>contenuto macro</i>",
|
||||||
|
ReplyID: &update.Message.MessageID,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,18 +44,14 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
||||||
// Not a message? Ignore
|
if !utils.IsCommand(update, m.name, "metafora") {
|
||||||
if update.Message == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.IsCommand(*update.Message, m.name, "metafora") {
|
m.client.SendTextMessage(tg.ClientTextMessageData{
|
||||||
m.client.SendTextMessage(tg.ClientTextMessageData{
|
ChatID: update.Message.Chat.ChatID,
|
||||||
ChatID: update.Message.Chat.ChatID,
|
Text: metaforaAPI(),
|
||||||
Text: metaforaAPI(),
|
})
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func metaforaAPI() string {
|
func metaforaAPI() string {
|
||||||
|
|
|
@ -46,18 +46,14 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
||||||
// Not a message? Ignore
|
if !utils.IsCommand(update, m.name, "proverbio") {
|
||||||
if update.Message == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.IsCommand(*update.Message, m.name, "proverbio") {
|
start := rand.Intn(len(proverbipairs.start))
|
||||||
start := rand.Intn(len(proverbipairs.start))
|
end := rand.Intn(len(proverbipairs.end))
|
||||||
end := rand.Intn(len(proverbipairs.end))
|
m.client.SendTextMessage(tg.ClientTextMessageData{
|
||||||
m.client.SendTextMessage(tg.ClientTextMessageData{
|
ChatID: update.Message.Chat.ChatID,
|
||||||
ChatID: update.Message.Chat.ChatID,
|
Text: fmt.Sprintf("<b>Dice il saggio:</b>\n%s %s", proverbipairs.start[start], proverbipairs.end[end]),
|
||||||
Text: fmt.Sprintf("<b>Dice il saggio:</b>\n%s %s", proverbipairs.start[start], proverbipairs.end[end]),
|
})
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
102
modules/remind/cmd.remind.go
Normal file
102
modules/remind/cmd.remind.go
Normal file
|
@ -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: "<b>Sintassi</b>\n/ricordami <i>[quando]</i> Messaggio\n\n<b>Formati supportati per [quando]</b>:\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: "<b>Heyla! Mi avevi chiesto di ricordarti questo:</b>\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()
|
||||||
|
}
|
123
modules/remind/cmd.reminders.go
Normal file
123
modules/remind/cmd.reminders.go
Normal file
|
@ -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("<b>Reminder %d</b> (/%d):\n%s\n<b>Quando:</b> %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
|
||||||
|
}
|
19
modules/remind/data.go
Normal file
19
modules/remind/data.go
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,8 @@ package remind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"git.fromouter.space/crunchy-rocks/clessy-ng/modules"
|
"git.fromouter.space/crunchy-rocks/clessy-ng/modules"
|
||||||
"git.fromouter.space/crunchy-rocks/clessy-ng/utils"
|
"git.fromouter.space/crunchy-rocks/clessy-ng/utils"
|
||||||
|
@ -15,14 +11,14 @@ import (
|
||||||
"git.fromouter.space/hamcha/tg"
|
"git.fromouter.space/hamcha/tg"
|
||||||
"git.sr.ht/~hamcha/containers"
|
"git.sr.ht/~hamcha/containers"
|
||||||
"github.com/cockroachdb/pebble"
|
"github.com/cockroachdb/pebble"
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reminder struct {
|
type Reminder struct {
|
||||||
TargetID int64
|
ReminderID string
|
||||||
When int64
|
TargetID int64
|
||||||
Text string
|
When int64
|
||||||
Reference *ReminderReference
|
Text string
|
||||||
|
Reference *ReminderReference
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReminderReference struct {
|
type ReminderReference struct {
|
||||||
|
@ -63,6 +59,20 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
|
||||||
return err
|
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 {
|
for id, reminder := range reminders {
|
||||||
pending.SetKey(id, reminder)
|
pending.SetKey(id, reminder)
|
||||||
go m.schedule(id)
|
go m.schedule(id)
|
||||||
|
@ -73,240 +83,18 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
||||||
// Not a message? Ignore
|
// Check for updates to existing messages
|
||||||
if update.Message == nil {
|
if update.Callback != nil {
|
||||||
return
|
m.handleCallback(update.Callback)
|
||||||
}
|
}
|
||||||
message := *update.Message
|
|
||||||
|
|
||||||
if utils.IsCommand(message, m.name, "ricordami") {
|
if utils.IsCommand(update, m.name, "ricordami") || utils.IsCommand(update, m.name, "remind") {
|
||||||
// Supported formats:
|
m.cmdRicordami(update.Message)
|
||||||
// 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: "<b>Sintassi</b>\n/ricordami <i>[quando]</i> Messaggio\n\n<b>Formati supportati per [quando]</b>:\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,
|
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.IsCommand(message, m.name, "reminders") {
|
if utils.IsCommand(update, m.name, "reminders") {
|
||||||
// Should only work in private chats
|
m.cmdReminder(update.Message)
|
||||||
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 <b>%d</b> 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: "<b>Heyla! Mi avevi chiesto di ricordarti questo:</b>\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())
|
|
||||||
return
|
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
|
|
||||||
}
|
|
||||||
|
|
103
modules/remind/utils.go
Normal file
103
modules/remind/utils.go
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -73,53 +73,21 @@ func (m *Module) Initialize(options modules.ModuleOptions) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
func (m *Module) OnUpdate(update tg.APIUpdate) {
|
||||||
// Not a message? Ignore
|
if !utils.IsCommand(update, m.name, "unsplash") {
|
||||||
if update.Message == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message := *update.Message
|
message := *update.Message
|
||||||
|
|
||||||
if utils.IsCommand(message, m.name, "unsplash") {
|
text := ""
|
||||||
text := ""
|
user := message.User
|
||||||
user := message.User
|
|
||||||
|
|
||||||
if message.ReplyTo != nil {
|
if message.ReplyTo != nil {
|
||||||
switch {
|
switch {
|
||||||
case message.ReplyTo.Text != nil:
|
case message.ReplyTo.Text != nil:
|
||||||
text = *(message.ReplyTo.Text)
|
text = *(message.ReplyTo.Text)
|
||||||
case message.ReplyTo.Caption != nil:
|
case message.ReplyTo.Caption != nil:
|
||||||
text = *(message.ReplyTo.Caption)
|
text = *(message.ReplyTo.Caption)
|
||||||
default:
|
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) == "" {
|
|
||||||
m.client.SendTextMessage(tg.ClientTextMessageData{
|
m.client.SendTextMessage(tg.ClientTextMessageData{
|
||||||
ChatID: message.Chat.ChatID,
|
ChatID: message.Chat.ChatID,
|
||||||
Text: "Non c'e' niente di 'ispiratore' in questo..",
|
Text: "Non c'e' niente di 'ispiratore' in questo..",
|
||||||
|
@ -128,137 +96,166 @@ func (m *Module) OnUpdate(update tg.APIUpdate) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Open(pics[rand.Intn(len(pics))])
|
// For forwarded message take the original user
|
||||||
if err != nil {
|
if message.FwdUser != nil {
|
||||||
log.Printf("[unsplash] Could not open original image file: %s\n", err.Error())
|
user = *message.FwdUser
|
||||||
m.client.SendTextMessage(tg.ClientTextMessageData{
|
} else {
|
||||||
ChatID: message.Chat.ChatID,
|
user = message.ReplyTo.User
|
||||||
Text: "<b>ERRORE!</b> @hamcha controlla la console!",
|
|
||||||
ReplyID: &message.MessageID,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer file.Close()
|
} else {
|
||||||
|
if strings.Index(*(message.Text), " ") > 0 {
|
||||||
img, _, err := image.Decode(file)
|
text = strings.TrimSpace(strings.SplitN(*(message.Text), " ", 2)[1])
|
||||||
if err != nil {
|
|
||||||
log.Printf("[unsplash] Image decode error: %s\n", err.Error())
|
|
||||||
m.client.SendTextMessage(tg.ClientTextMessageData{
|
|
||||||
ChatID: message.Chat.ChatID,
|
|
||||||
Text: "<b>ERRORE!</b> @hamcha controlla la console!",
|
|
||||||
ReplyID: &message.MessageID,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m.client.SendChatAction(tg.ClientChatActionData{
|
// Cleanup chars
|
||||||
ChatID: message.Chat.ChatID,
|
text = strings.Map(stripUnreadable, text)
|
||||||
Action: tg.ActionUploadingPhoto,
|
|
||||||
|
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
|
file, err := os.Open(pics[rand.Intn(len(pics))])
|
||||||
img = imaging.AdjustBrightness(imaging.AdjustGamma(imaging.AdjustSigmoid(img, 0.5, -6.0), 0.8), -20)
|
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: "<b>ERRORE!</b> @hamcha controlla la console!",
|
||||||
|
ReplyID: &message.MessageID,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
// Create target image
|
img, _, err := image.Decode(file)
|
||||||
bounds := img.Bounds()
|
if err != nil {
|
||||||
iwidth := float64(bounds.Size().X)
|
log.Printf("[unsplash] Image decode error: %s\n", err.Error())
|
||||||
iheight := float64(bounds.Size().Y)
|
m.client.SendTextMessage(tg.ClientTextMessageData{
|
||||||
|
ChatID: message.Chat.ChatID,
|
||||||
|
Text: "<b>ERRORE!</b> @hamcha controlla la console!",
|
||||||
|
ReplyID: &message.MessageID,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
timg := image.NewRGBA(bounds)
|
m.client.SendChatAction(tg.ClientChatActionData{
|
||||||
gc := draw2dimg.NewGraphicContext(timg)
|
ChatID: message.Chat.ChatID,
|
||||||
gc.Emojis = m.emojis
|
Action: tg.ActionUploadingPhoto,
|
||||||
gc.SetFontData(quoteFontData)
|
})
|
||||||
gc.DrawImage(img)
|
|
||||||
gc.SetStrokeColor(image.Black)
|
|
||||||
gc.SetFillColor(image.White)
|
|
||||||
|
|
||||||
text = strings.ToUpper(strings.TrimSpace(text))
|
// Darken image
|
||||||
gc.Restore()
|
img = imaging.AdjustBrightness(imaging.AdjustGamma(imaging.AdjustSigmoid(img, 0.5, -6.0), 0.8), -20)
|
||||||
gc.Save()
|
|
||||||
|
|
||||||
// Detect appropriate font size
|
// Create target image
|
||||||
scale := iheight / iwidth * (iwidth / 10) * 0.8
|
bounds := img.Bounds()
|
||||||
gc.SetFontSize(scale)
|
iwidth := float64(bounds.Size().X)
|
||||||
gc.SetLineWidth(scale / 15)
|
iheight := float64(bounds.Size().Y)
|
||||||
|
|
||||||
// Get NEW bounds
|
timg := image.NewRGBA(bounds)
|
||||||
left, top, right, bottom := gc.GetStringBounds(text)
|
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
|
text = strings.ToUpper(strings.TrimSpace(text))
|
||||||
texts := []string{text}
|
gc.Restore()
|
||||||
if width*1.2 > iwidth {
|
gc.Save()
|
||||||
// Split text
|
|
||||||
texts = utils.SplitCenter(text)
|
|
||||||
|
|
||||||
// Get longest line
|
// Detect appropriate font size
|
||||||
longer := float64(0)
|
scale := iheight / iwidth * (iwidth / 10) * 0.8
|
||||||
longid := 0
|
gc.SetFontSize(scale)
|
||||||
widths := make([]float64, len(texts))
|
gc.SetLineWidth(scale / 15)
|
||||||
for id := range texts {
|
|
||||||
tleft, _, tright, _ := gc.GetStringBounds(texts[id])
|
|
||||||
widths[id] = tright - tleft
|
|
||||||
if width > longer {
|
|
||||||
longer = widths[id]
|
|
||||||
longid = id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Still too big? Decrease font size again
|
// Get NEW bounds
|
||||||
iter := 0
|
left, top, right, bottom := gc.GetStringBounds(text)
|
||||||
for width*1.2 > iwidth && iter < 10 {
|
|
||||||
scale *= (0.9 - 0.05*float64(iter))
|
width := right - left
|
||||||
gc.SetFontSize(scale)
|
texts := []string{text}
|
||||||
left, top, right, bottom = gc.GetStringBounds(texts[longid])
|
if width*1.2 > iwidth {
|
||||||
width = right - left
|
// Split text
|
||||||
iter++
|
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)
|
// Still too big? Decrease font size again
|
||||||
height := bottom - top + 20
|
iter := 0
|
||||||
margin := float64(height / 50)
|
for width*1.2 > iwidth && iter < 10 {
|
||||||
txtheight := (height + margin) * float64(len(texts))
|
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()
|
gc.Save()
|
||||||
for id, txt := range texts {
|
left, _, right, _ = gc.GetStringBounds(txt)
|
||||||
gc.Save()
|
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)
|
left, _, right, _ = gc.GetStringBounds(txt)
|
||||||
width = right - left
|
width = right - left
|
||||||
|
x = (iwidth - width) / 1.5
|
||||||
x := (iwidth - width) / 2
|
y = (iheight-txtheight)/2 + (height+margin)*float64(id+1) + margin*6
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
gc.Translate(x, y)
|
||||||
err = jpeg.Encode(buf, timg, &(jpeg.Options{Quality: 80}))
|
gc.StrokeString(txt)
|
||||||
if err != nil {
|
gc.FillString(txt)
|
||||||
log.Printf("[unsplash] Image encode error: %s\n", err.Error())
|
gc.Restore()
|
||||||
m.client.SendTextMessage(tg.ClientTextMessageData{
|
|
||||||
ChatID: message.Chat.ChatID,
|
|
||||||
Text: "<b>ERRORE!</b> @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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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: "<b>ERRORE!</b> @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 {
|
func stripUnreadable(r rune) rune {
|
||||||
|
|
|
@ -6,12 +6,17 @@ import (
|
||||||
"git.fromouter.space/hamcha/tg"
|
"git.fromouter.space/hamcha/tg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsCommand(update tg.APIMessage, botname string, cmdname string) bool {
|
func IsCommand(update tg.APIUpdate, botname string, cmdname string) bool {
|
||||||
if update.Text == nil {
|
if update.Message == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
message := update.Message
|
||||||
|
|
||||||
|
if message.Text == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
text := strings.TrimSpace(*(update.Text))
|
text := strings.TrimSpace(*(message.Text))
|
||||||
|
|
||||||
shortcmd := "/" + cmdname
|
shortcmd := "/" + cmdname
|
||||||
fullcmd := shortcmd + "@" + botname
|
fullcmd := shortcmd + "@" + botname
|
||||||
|
|
Loading…
Reference in a new issue