149 lines
3.5 KiB
Go
149 lines
3.5 KiB
Go
package plugins
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/exp/slog"
|
|
)
|
|
|
|
const (
|
|
timeFormat = "02.01.2006 15:04:05"
|
|
dateFormat = "02.01.2006"
|
|
)
|
|
|
|
var (
|
|
locationNames = []string{"UTC", "MSK", "CY", "ME"}
|
|
locations = map[string]*time.Location{
|
|
"UTC": loadLocation("UTC"),
|
|
"CY": loadLocation("Asia/Nicosia"),
|
|
"ME": loadLocation("Europe/Podgorica"),
|
|
"BG": loadLocation("Europe/Belgrade"),
|
|
"MSK": loadLocation("Europe/Moscow"),
|
|
}
|
|
locationIcons = map[string]string{
|
|
"UTC": "🕰",
|
|
"CY": "🏝",
|
|
"ME": "⛰",
|
|
"BG": "☕️",
|
|
"MSK": "🔺",
|
|
}
|
|
)
|
|
|
|
type WorldClockPlugin struct{}
|
|
|
|
func NewWorldClockPlugin() *WorldClockPlugin {
|
|
return &WorldClockPlugin{}
|
|
}
|
|
|
|
func (p *WorldClockPlugin) CanHandle(msgText string) bool {
|
|
_, err := p.parseDate(msgText)
|
|
if err == nil {
|
|
return true
|
|
}
|
|
|
|
_, err = p.parseTime(msgText)
|
|
if err == nil {
|
|
return true
|
|
}
|
|
|
|
_, err = p.parseTimestamp(msgText)
|
|
if err == nil {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Handle checks if the message is a date, time or timestamp and sends the current time in different timezones
|
|
func (p *WorldClockPlugin) Handle(msgText string) (string, error) {
|
|
var message string
|
|
var err error
|
|
|
|
// Try to parse date
|
|
t, err := p.parseDate(msgText)
|
|
if err == nil {
|
|
message = p.buildMessage(t, p.fmtTimestamp)
|
|
return message, nil
|
|
}
|
|
|
|
// Try to parse time
|
|
t, err = p.parseTime(msgText)
|
|
if err == nil {
|
|
message = p.buildMessage(t, p.fmtTimestamp)
|
|
return message, nil
|
|
}
|
|
|
|
// Try to parse timestamp
|
|
t, err = p.parseTimestamp(msgText)
|
|
if err == nil {
|
|
message = p.buildMessage(t, p.fmtTime)
|
|
return message, nil
|
|
}
|
|
|
|
return "", nil
|
|
}
|
|
|
|
func (p *WorldClockPlugin) parseTimestamp(message string) (time.Time, error) {
|
|
timestamp, err := strconv.ParseInt(message, 10, 64)
|
|
if err == nil && timestamp > 999999 {
|
|
// Check if it's microseconds (16 digits), milliseconds (13 digits) or seconds (10 digits)
|
|
if timestamp > 9999999999999 {
|
|
return time.Unix(timestamp/1000000, (timestamp%1000000)*1000).UTC(), nil
|
|
} else if timestamp > 9999999999 {
|
|
return time.Unix(timestamp/1000, (timestamp%1000)*1000000).UTC(), nil
|
|
}
|
|
return time.Unix(timestamp, 0).UTC(), nil
|
|
}
|
|
return time.Time{}, errors.New("invalid timestamp")
|
|
}
|
|
|
|
func (p *WorldClockPlugin) parseTime(message string) (time.Time, error) {
|
|
parsedTime, err := time.Parse(timeFormat, message)
|
|
if err == nil {
|
|
return parsedTime.UTC(), nil
|
|
}
|
|
return time.Time{}, errors.New("invalid time")
|
|
}
|
|
|
|
func (p *WorldClockPlugin) parseDate(message string) (time.Time, error) {
|
|
parsedDate, err := time.Parse(dateFormat, message)
|
|
if err == nil {
|
|
return parsedDate.UTC(), nil
|
|
}
|
|
return time.Time{}, errors.New("invalid date")
|
|
}
|
|
|
|
func (p *WorldClockPlugin) buildMessage(t time.Time, formatter func(time.Time) string) string {
|
|
messageParts := make([]string, len(locations))
|
|
|
|
for i, locName := range locationNames {
|
|
timeInLocation := t.In(locations[locName])
|
|
formattedTime := formatter(timeInLocation)
|
|
messageParts[i] = fmt.Sprintf("%v %v %v", locationIcons[locName], formattedTime, locName)
|
|
}
|
|
return strings.Join(messageParts, "\n")
|
|
}
|
|
|
|
func (p *WorldClockPlugin) fmtTime(t time.Time) string {
|
|
return t.Format(timeFormat)
|
|
}
|
|
|
|
func (p *WorldClockPlugin) fmtTimestamp(t time.Time) string {
|
|
_, offset := t.Zone()
|
|
timestampInLoc := t.Add(time.Duration(offset) * time.Second).Unix()
|
|
return strconv.FormatInt(timestampInLoc, 10)
|
|
}
|
|
|
|
func loadLocation(name string) *time.Location {
|
|
location, err := time.LoadLocation(name)
|
|
if err != nil {
|
|
slog.Warn("Error loading location", err)
|
|
return nil
|
|
}
|
|
return location
|
|
}
|