/
replace.go
217 lines (203 loc) · 5.88 KB
/
replace.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package replace
import (
"database/sql"
"fmt"
"github.com/go-gorp/gorp"
_ "github.com/go-sql-driver/mysql"
_ "github.com/mattn/go-sqlite3"
"log"
"regexp"
"strings"
"time"
)
type Props struct {
ID uint `db:"id, primarykey, autoincrement"`
CreatedAt time.Time `db:"created_at, notnull"`
UpdatedAt time.Time `db:"updated_at, notnull"`
}
type UserDictInput struct {
Word string `db:"word, notnull"`
Yomi string `db:"yomi, notnull"`
ChangedUserId string `db:"changed_user_id, notnull"`
GuildId string `db:"guild_id, notnull"`
}
type Dict struct {
Props
UserDictInput
}
type Replacer struct {
gorpDb *gorp.DbMap
guildId string
}
type DbController interface {
Add(dict *UserDictInput) error
Delete(dictId uint) (Dict, error)
ApplyUserDict(msg string) (string, error)
SetGuildId(guildId string)
Dump(limits uint) ([]Dict, error)
DumpAtoB(from, to uint) ([]Dict, error)
}
func initDb(db *sql.DB, dialect gorp.Dialect) (*gorp.DbMap, error) {
dbMap := &gorp.DbMap{Db: db, Dialect: dialect}
dbMap.AddTableWithName(Dict{}, "dicts").SetKeys(true, "id")
err := dbMap.CreateTablesIfNotExists()
if err != nil {
log.Println(fmt.Errorf("create table failed `%w`", err))
return nil, err
}
return dbMap, nil
}
func NewReplacer(db *sql.DB, dialect gorp.Dialect) (*Replacer, error) {
rs := &Replacer{}
dbMap, err := initDb(db, dialect)
if err != nil {
return nil, err
}
rs.gorpDb = dbMap
return rs, nil
}
// TODO; guildIdを受け取り、そのGuildIDを保存する構造体を作成。その構造体にAddやdelのメソッドを実装する。
// AddやDelは使い捨ての構造体のメソッドに
func (rs *Replacer) SetGuildId(guildId string) { rs.guildId = guildId }
func (rs *Replacer) Add(dict *UserDictInput) error {
var findRes []Dict
_, err := rs.gorpDb.Select(&findRes, "select * from dicts where word = ? and guild_id = ? order by updated_at desc;",
dict.Word, dict.GuildId)
if err != nil {
return fmt.Errorf("upsert failed `%w`", err)
}
isExist := len(findRes) != 0
if !isExist {
insertDict := Dict{
Props: Props{ID: 0, CreatedAt: time.Now(), UpdatedAt: time.Now()},
UserDictInput: *dict,
}
err := rs.gorpDb.Insert(&insertDict)
if err != nil {
return fmt.Errorf("insert failed `%w`", err)
}
return nil
} else {
if len(findRes) > 1 {
i, err := rs.gorpDb.Delete(findRes[1:])
if i == 0 {
log.Printf("want to delete dupricate record but not deleted")
}
if err != nil {
return fmt.Errorf("deplicate record delete err `%w`", err)
}
}
updateDict := findRes[0]
updateDict.UpdatedAt = time.Now()
updateDict.UserDictInput = *dict
i, err := rs.gorpDb.Update(&updateDict)
if i == 0 {
return fmt.Errorf("no update execute")
}
if err != nil {
return fmt.Errorf("update execute failed `%w`", err)
}
return nil
}
}
func (rs *Replacer) Delete(dictId uint) (Dict, error) {
dict := Dict{}
err := rs.gorpDb.SelectOne(&dict, "select * from dicts where guild_id = ? and id = ?", rs.guildId, dictId)
if err != nil {
return Dict{}, fmt.Errorf("record not found `%w`", err)
}
_, err = rs.gorpDb.Delete(&dict)
return dict, err
}
func (rs *Replacer) ApplyUserDict(msg string) (string, error) {
var records []Dict
_, err := rs.gorpDb.Select(&records, "select * from dicts where guild_id = ? order by length(word) desc, updated_at desc;", rs.guildId)
if err != nil {
return msg, fmt.Errorf("retrieve user dict failed `%w`", err)
}
d := Dicts(records)
return d.replace(msg), nil
}
func (rs *Replacer) Dump(limit uint) ([]Dict, error) {
var dictList []Dict
_, err := rs.gorpDb.Select(&dictList, "select * from dicts where guild_id = ? order by updated_at desc limit ?", rs.guildId, limit)
if err != nil {
return nil, fmt.Errorf("dump dict error `%w`", err)
}
return dictList, nil
}
func (rs *Replacer) DumpAtoB(from, to uint) ([]Dict, error) {
var dictList []Dict
_, err := rs.gorpDb.Select(&dictList, "select * from dicts where guild_id = ? and id >=? and id <=? order by id", rs.guildId, from, to)
if err != nil {
return nil, fmt.Errorf("dump dict error `%w`", err)
}
return dictList, nil
}
func ApplySysDict(msg string) string {
type dict struct {
before *regexp.Regexp
after string
}
dicts := []dict{
{before: regexp.MustCompile(`https?://.*`), after: "URL "},
{before: regexp.MustCompile("(?s)```(.*)```"), after: "コードブロック"},
{before: regexp.MustCompile("~~(.+)~~"), after: " ピーー"},
{before: regexp.MustCompile("\n"), after: " "},
{before: regexp.MustCompile("~"), after: "ー"},
{before: regexp.MustCompile("〜"), after: "ー"},
}
for _, d := range dicts {
msg = d.before.ReplaceAllString(msg, d.after)
}
return replaceCustomEmoji(msg)
}
func replaceCustomEmoji(msg string) string {
return regexp.MustCompile(`<a?:.*:.*>`).ReplaceAllString(msg, "")
}
type Dicts []Dict
func (ds *Dicts) GetStringSlice() []string {
var res []string
res = append(res, "ID\t単語\tよみ", "---------------------------")
for _, v := range *ds {
res = append(res, fmt.Sprintf("%d\t%s\t%s", v.ID, v.Word, v.Yomi))
}
return res
}
func (ds *Dicts) replace(msg string) string {
rMsg := []rune(msg)
for cur := 0; cur < len(rMsg); {
isReplaced := false
for _, record := range *ds {
befLen := len([]rune(record.Word))
if cur+befLen > len(rMsg) {
continue
}
if !strings.EqualFold(string(rMsg[cur:cur+befLen]), record.Word) {
continue
}
rMsg = append(rMsg[0:cur],
[]rune(strings.Replace(
toLowerSubStr(string(rMsg[cur:]), 0, befLen),
strings.ToLower(record.Word),
record.Yomi,
1),
)...)
cur += len([]rune(record.Yomi))
isReplaced = true
break
}
if !isReplaced {
cur++
}
}
return string(rMsg)
}
func toLowerSubStr(s string, start, end int) string {
rs := []rune(s)
res := rs[0:start]
replaced := []rune(strings.ToLower(string(rs[start:end])))
res = append(res, replaced...)
res = append(res, rs[end:]...)
return string(res)
}