-
Notifications
You must be signed in to change notification settings - Fork 2
/
message.go
118 lines (96 loc) · 3.06 KB
/
message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package types
import (
"reflect"
"strconv"
"time"
"github.com/mitchellh/mapstructure"
)
const (
// SourceUnknown is where we simply ust don't know where a message comes from,
// and is largely only used for zero'd messages, or when errors stop the
// processing of messages.
SourceUnknown Source = iota
// SourceWhatsapp means a message has come from WhatsApp and usually signifies
// a message from someone seeking advice
SourceWhatsapp
// SourceAutoresponse means a message was generated from an application in the
// processing pipeline somewhere, like the various autoresponses the gec-bot
// provides
SourceAutoresponse
// SourceSlack usually means a message from the GEC _back_ to recipients; though
// in the future perhaps we'd want to allow slack users to message too...
// dunno
SourceSlack
)
// Source signifies the source of a message; whether it's come
// in from whatsapp, slack, some kind of auto-responder, or just
// completely unknown
type Source uint8
// MarshalBinary implements the encoding.BinaryMarshaler interface
// in order to serialise data to redis correctly
func (s Source) MarshalBinary() ([]byte, error) {
return []byte(strconv.Itoa(int(s))), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
// in order to serialise data _from_ redis correctly
func (s *Source) UnmarshalBinary(data []byte) error {
i, err := strconv.Atoi(string(data))
if err != nil {
*s = SourceUnknown
return nil
}
*s = Source(i)
return nil
}
// Message is, simply, the message to be passed between recipients
type Message struct {
Source Source `mapstructure:"source"`
ID string `mapstructure:"id"`
Timestamp int64 `mapstructure:"ts"`
Message string `mapstructure:"msg"`
Sentiment int `mapstructure:"sentiment"`
}
// NewMessage accepts a source, id and string, and returns a new Message
func NewMessage(source Source, id, msg string) Message {
return Message{
Source: source,
ID: id,
Timestamp: time.Now().Unix(),
Message: msg,
}
}
// ParseMessage accepts a map, probably from redis, and turns it into a valid
// Message for processing
func ParseMessage(i map[string]any) (m Message, err error) {
// If inputs are strings, and we're not expecting strings, such
// as Source and Timestamp, then cast them properly.
//
// This happens when we read from redis, which treats all values
// as strings
cast(i, "source")
cast(i, "ts")
cast(i, "sentiment")
err = mapstructure.Decode(i, &m)
return
}
// Map returns a map from the message to be placed on a redis XSTREAM (etc.)
func (m Message) Map() (o map[string]any) {
_ = mapstructure.Decode(m, &o)
return
}
// GetTimestamp takes the unixtimestamp stored within a Message and returns
// a localised time.Time based on this.
//
// Note: to get the time in UTC you'll need to do
//
// m.GetTimestamp().UTC()
func (m Message) GetTimestamp() time.Time {
return time.Unix(m.Timestamp, 0)
}
func cast(i map[string]any, key string) {
v, ok := i[key]
if !ok || reflect.TypeOf(v).String() != "string" {
return
}
i[key], _ = strconv.Atoi(i[key].(string))
}