feat: unified chat API pt. 3 (#2535)

This commit is contained in:
Richard Ramos 2022-02-25 13:52:03 -04:00
parent f9b87c4ae1
commit a15c27c588
16 changed files with 42874 additions and 4 deletions

3
go.mod
View File

@ -10,12 +10,15 @@ replace github.com/docker/docker => github.com/docker/engine v1.4.2-0.2019071716
replace github.com/nfnt/resize => github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088
replace github.com/forPelevin/gomoji => github.com/status-im/gomoji v1.1.3-0.20220213022530-e5ac4a8732d4
require (
github.com/beevik/ntp v0.2.0
github.com/cenkalti/backoff/v3 v3.2.2
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v1.7.1
github.com/ethereum/go-ethereum v1.10.13
github.com/forPelevin/gomoji v1.1.2
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/golang-migrate/migrate/v4 v4.8.0 // indirect
github.com/golang/mock v1.6.0

2
go.sum
View File

@ -1218,6 +1218,8 @@ github.com/status-im/go-waku v0.0.0-20220303160403-f4f307db8734 h1:uyBsGFFH63+SH
github.com/status-im/go-waku v0.0.0-20220303160403-f4f307db8734/go.mod h1:7SH3xYPUpUwiwSkSSuqPoJQo01ulLD5hSFDvGZzjsvo=
github.com/status-im/go-waku-rendezvous v0.0.0-20211018070416-a93f3b70c432 h1:cbNFU38iimo9fY4B7CdF/fvIF6tNPJIZjBbpfmW2EY4=
github.com/status-im/go-waku-rendezvous v0.0.0-20211018070416-a93f3b70c432/go.mod h1:A8t3i0CUGtXCA0aiLsP7iyikmk/KaD/2XVvNJqGCU20=
github.com/status-im/gomoji v1.1.3-0.20220213022530-e5ac4a8732d4 h1:CtobZoiNdHpx+xurFxnuJ1xsGm3oKMfcZkB3vmomJmA=
github.com/status-im/gomoji v1.1.3-0.20220213022530-e5ac4a8732d4/go.mod h1:hmpnZzkzSZJbFYWAUkrPV8I36x7mdYiPhPqnALP4fKA=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 h1:Oo2KZNP70KE0+IUJSidPj/BFS/RXNHmKIJOdckzml2E=
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=

View File

@ -693,6 +693,14 @@ func (db *Database) GetPublicKey() (rst string, err error) {
return
}
func (db *Database) GetPreferredUsername() (rst string, err error) {
err = db.db.QueryRow("SELECT preferred_name FROM settings WHERE synthetic_id = 'id'").Scan(&rst)
if err == sql.ErrNoRows {
return rst, nil
}
return
}
func (db *Database) GetFleet() (rst string, err error) {
err = db.db.QueryRow("SELECT COALESCE(fleet, '') FROM settings WHERE synthetic_id = 'id'").Scan(&rst)
if err == sql.ErrNoRows {

View File

@ -390,7 +390,7 @@ func (b *StatusNode) gifService() *gif.Service {
func (b *StatusNode) ChatService() *chat.Service {
if b.chatSrvc == nil {
b.chatSrvc = chat.NewService()
b.chatSrvc = chat.NewService(b.appDB)
}
return b.chatSrvc
}

View File

@ -0,0 +1,159 @@
package chat
import (
"context"
"github.com/forPelevin/gomoji"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
)
type SendMessageResponse struct {
Chat *Chat `json:"chat"`
Messages []*common.Message `json:"messages"`
}
func (api *API) SendSticker(ctx context.Context, communityID types.HexBytes, chatID string, packID int32, hash string, responseTo string) (*SendMessageResponse, error) {
ensName, _ := api.s.accountsDB.GetPreferredUsername()
msg := &common.Message{
CommunityID: string(communityID.Bytes()),
ChatMessage: protobuf.ChatMessage{
ChatId: chatID,
ContentType: protobuf.ChatMessage_STICKER,
Text: "Update to latest version to see a nice sticker here!",
Payload: &protobuf.ChatMessage_Sticker{
Sticker: &protobuf.StickerMessage{
Hash: hash,
Pack: packID,
},
},
ResponseTo: responseTo,
EnsName: ensName,
},
}
response, err := api.s.messenger.SendChatMessage(ctx, msg)
if err != nil {
return nil, err
}
return api.toSendMessageResponse(response)
}
func (api *API) toSendMessageResponse(response *protocol.MessengerResponse) (*SendMessageResponse, error) {
protocolChat := response.Chats()[0]
community, err := api.s.messenger.GetCommunityByID(types.HexBytes(protocolChat.CommunityID))
if err != nil {
return nil, err
}
pubKey := types.EncodeHex(crypto.FromECDSAPub(api.s.messenger.IdentityPublicKey()))
chat, err := api.toAPIChat(protocolChat, community, pubKey)
if err != nil {
return nil, err
}
return &SendMessageResponse{
Chat: chat,
Messages: response.Messages(),
}, nil
}
func isTextOrEmoji(text string) protobuf.ChatMessage_ContentType {
contentType := protobuf.ChatMessage_TEXT_PLAIN
if gomoji.RemoveEmojis(text) == "" && len(gomoji.FindAll(text)) != 0 {
contentType = protobuf.ChatMessage_EMOJI
}
return contentType
}
func (api *API) SendMessage(ctx context.Context, communityID types.HexBytes, chatID string, text string, responseTo string) (*SendMessageResponse, error) {
ensName, _ := api.s.accountsDB.GetPreferredUsername()
msg := &common.Message{
CommunityID: string(communityID.Bytes()),
ChatMessage: protobuf.ChatMessage{
ChatId: chatID,
ContentType: isTextOrEmoji(text),
Text: text,
ResponseTo: responseTo,
EnsName: ensName,
},
}
response, err := api.s.messenger.SendChatMessage(ctx, msg)
if err != nil {
return nil, err
}
return api.toSendMessageResponse(response)
}
func (api *API) SendImages(ctx context.Context, communityID types.HexBytes, chatID string, imagePaths []string, text string) (*SendMessageResponse, error) {
ensName, _ := api.s.accountsDB.GetPreferredUsername()
var messages []*common.Message
for _, imagePath := range imagePaths {
messages = append(messages, &common.Message{
CommunityID: string(communityID.Bytes()),
ChatMessage: protobuf.ChatMessage{
ChatId: chatID,
ContentType: protobuf.ChatMessage_IMAGE,
Text: "Update to latest version to see a nice image here!",
EnsName: ensName,
},
ImagePath: imagePath,
})
}
if text != "" {
messages = append(messages, &common.Message{
CommunityID: string(communityID.Bytes()),
ChatMessage: protobuf.ChatMessage{
ChatId: chatID,
ContentType: isTextOrEmoji(text),
Text: text,
EnsName: ensName,
},
})
}
response, err := api.s.messenger.SendChatMessages(ctx, messages)
if err != nil {
return nil, err
}
return api.toSendMessageResponse(response)
}
func (api *API) SendAudio(ctx context.Context, communityID types.HexBytes, chatID string, audioPath string, responseTo string) (*SendMessageResponse, error) {
ensName, _ := api.s.accountsDB.GetPreferredUsername()
msg := &common.Message{
CommunityID: string(communityID.Bytes()),
ChatMessage: protobuf.ChatMessage{
ChatId: chatID,
Text: "Update to latest version to listen to an audio message here!",
ContentType: protobuf.ChatMessage_AUDIO,
ResponseTo: responseTo,
EnsName: ensName,
},
AudioPath: audioPath,
}
response, err := api.s.messenger.SendChatMessage(ctx, msg)
if err != nil {
return nil, err
}
return api.toSendMessageResponse(response)
}

View File

@ -1,18 +1,24 @@
package chat
import (
"database/sql"
"github.com/ethereum/go-ethereum/p2p"
gethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/protocol"
)
func NewService() *Service {
return &Service{}
func NewService(appDB *sql.DB) *Service {
return &Service{
accountsDB: accounts.NewDB(appDB),
}
}
type Service struct {
messenger *protocol.Messenger
messenger *protocol.Messenger
accountsDB *accounts.Database
}
func (s *Service) Init(messenger *protocol.Messenger) {

17
vendor/github.com/forPelevin/gomoji/.gitignore generated vendored Normal file
View File

@ -0,0 +1,17 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
.idea
.DS_Store
vendor

105
vendor/github.com/forPelevin/gomoji/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,105 @@
# options for analysis running
run:
# default concurrency is a available CPU number
concurrency: 1
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 5m
# exit code when at least one issue was found, default is 1
issues-exit-code: 1
# include test files or not, default is true
tests: true
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
skip-files:
issues:
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`.
# Default value for this option is true.
exclude-use-default: false
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
format: colored-line-number
# print lines of code with issue, default is true
print-issued-lines: true
# print linter name in the end of issue text, default is true
print-linter-name: true
# all available settings of specific linters
linters-settings:
govet:
# report about shadowed variables
check-shadowing: true
golint:
# minimal confidence for issues, default is 0.8
min-confidence: 0.8
gofmt:
# simplify code: gofmt with `-s` option, true by default
simplify: true
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: true
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 20
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
unparam:
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
nakedret:
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
max-func-lines: 30
prealloc:
# XXX: we don't recommend using this linter before doing performance profiling.
# For most programs usage of prealloc will be a premature optimization.
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
# True by default.
simple: true
range-loops: true # Report preallocation suggestions on range loops, true by default
for-loops: false # Report preallocation suggestions on for loops, false by default
linters:
disable-all: true
enable:
- megacheck
- nakedret
- unparam
- unused
- govet
- golint
- gofmt
- errcheck
- gocyclo
- ineffassign
- deadcode
- goimports
fast: false

21
vendor/github.com/forPelevin/gomoji/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Vladislav Gukasov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
vendor/github.com/forPelevin/gomoji/Makefile generated vendored Normal file
View File

@ -0,0 +1,13 @@
GO_FILES=$(shell find . -name '*.go' | grep -vE 'vendor')
lint-fix:
go get github.com/golangci/golangci-lint
goimports -w $(GO_FILES)
go fmt ./...
golangci-lint -v run ./...
test:
go test -count 1 -v -race ./...
bench:
go test -bench=. -benchmem -v -run Benchmark ./...

193
vendor/github.com/forPelevin/gomoji/README.md generated vendored Normal file
View File

@ -0,0 +1,193 @@
# GoMoji
<p align="center">work with emoji in the most convenient way</p>
GoMoji is a Go package that provides a [fast](#performance) and [simple](#check-string-contains-emoji) way to work with emojis in strings.
It has features such as:
* [check whether string contains emoji](#check-string-contains-emoji)
* [find all emojis in string](#find-all)
* [get all emojis](#get-all)
* [remove all emojis from string](#remove-all-emojis)
* [get emoji description](#get-emoji-info)
Getting Started
===============
## Installing
To start using GoMoji, install Go and run `go get`:
```sh
$ go get -u github.com/forPelevin/gomoji
```
This will retrieve the package.
## Check string contains emoji
```go
package main
import (
"github.com/forPelevin/gomoji"
)
func main() {
res := gomoji.ContainsEmoji("hello world")
println(res) // false
res = gomoji.ContainsEmoji("hello world 🤗")
println(res) // true
}
```
## Find all
The function searches for all emoji occurrences in a string. It returns a nil slice if there are no emojis.
```go
package main
import (
"github.com/forPelevin/gomoji"
)
func main() {
res := gomoji.FindAll("🧖 hello 🦋 world")
println(res)
}
```
Result:
```go
[]gomoji.Emoji{
{
Slug: "person-in-steamy-room",
Character: "🧖",
UnicodeName: "E5.0 person in steamy room",
CodePoint: "1F9D6",
Group: "People & Body",
SubGroup: "person-activity",
},
{
Slug: "butterfly",
Character: "🦋",
UnicodeName: "E3.0 butterfly",
CodePoint: "1F98B",
Group: "Animals & Nature",
SubGroup: "animal-bug",
},
}
```
## Get all
The function returns all existing emojis. You can do whatever you need with the list.
```go
package main
import (
"github.com/forPelevin/gomoji"
)
func main() {
emojis := gomoji.AllEmojis()
println(emojis)
}
```
## Remove all emojis
The function removes all emojis from given string:
```go
res := gomoji.RemoveEmojis("🧖 hello 🦋world")
println(res) // "hello world"
```
## Get emoji info
The function returns info about provided emoji:
```go
info, err := gomoji.GetInfo("1") // error: the string is not emoji
info, err := gomoji.GetInfo("1⃣")
println(info)
```
Result:
```go
gomoji.Entity{
Slug: "keycap-1",
Character: "1⃣",
UnicodeName: "E0.6 keycap: 1",
CodePoint: "0031 FE0F 20E3",
Group: "Symbols",
SubGroup: "keycap",
}
```
## Emoji entity
All searching methods return the Emoji entity which contains comprehensive info about emoji.
```go
type Emoji struct {
Slug string `json:"slug"`
Character string `json:"character"`
UnicodeName string `json:"unicode_name"`
CodePoint string `json:"code_point"`
Group string `json:"group"`
SubGroup string `json:"sub_group"`
}
```
Example:
```go
[]gomoji.Emoji{
{
Slug: "butterfly",
Character: "🦋",
UnicodeName: "E3.0 butterfly",
CodePoint: "1F98B",
Group: "Animals & Nature",
SubGroup: "animal-bug",
},
{
Slug: "roll-of-paper",
Character: "🧻",
UnicodeName: "E11.0 roll of paper",
CodePoint: "1F9FB",
Group: "Objects",
SubGroup: "household",
},
}
```
## Performance
GoMoji Benchmarks
```
goos: darwin
goarch: amd64
pkg: github.com/forPelevin/gomoji
cpu: Intel(R) Core(TM) i5-8257U CPU @ 1.40GHz
BenchmarkContainsEmojiParallel
BenchmarkContainsEmojiParallel-8 7439398 159.2 ns/op 144 B/op 3 allocs/op
BenchmarkContainsEmoji
BenchmarkContainsEmoji-8 2457042 482.2 ns/op 144 B/op 3 allocs/op
BenchmarkRemoveEmojisParallel
BenchmarkRemoveEmojisParallel-8 4589841 265.8 ns/op 236 B/op 5 allocs/op
BenchmarkRemoveEmojis
BenchmarkRemoveEmojis-8 1456464 831.9 ns/op 236 B/op 5 allocs/op
BenchmarkGetInfoParallel
BenchmarkGetInfoParallel-8 272416886 4.433 ns/op 0 B/op 0 allocs/op
BenchmarkGetInfo
BenchmarkGetInfo-8 64521932 19.86 ns/op 0 B/op 0 allocs/op
BenchmarkFindAllParallel
BenchmarkFindAllParallel-8 3989124 295.9 ns/op 456 B/op 5 allocs/op
BenchmarkFindAll
BenchmarkFindAll-8 1304463 913.7 ns/op 456 B/op 5 allocs/op
```
## Contact
Vlad Gukasov [@vgukasov](https://www.facebook.com/vgukasov)
## License
GoMoji source code is available under the MIT [License](/LICENSE).

42245
vendor/github.com/forPelevin/gomoji/data.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

5
vendor/github.com/forPelevin/gomoji/go.mod generated vendored Normal file
View File

@ -0,0 +1,5 @@
module github.com/forPelevin/gomoji
go 1.13
require github.com/rivo/uniseg v0.2.0

2
vendor/github.com/forPelevin/gomoji/go.sum generated vendored Normal file
View File

@ -0,0 +1,2 @@
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=

89
vendor/github.com/forPelevin/gomoji/gomoji.go generated vendored Normal file
View File

@ -0,0 +1,89 @@
package gomoji
import (
"bytes"
"errors"
"strings"
"github.com/rivo/uniseg"
)
// errors
var (
ErrStrNotEmoji = errors.New("the string is not emoji")
)
// Emoji is an entity that represents comprehensive emoji info.
type Emoji struct {
Slug string `json:"slug"`
Character string `json:"character"`
UnicodeName string `json:"unicode_name"`
CodePoint string `json:"code_point"`
Group string `json:"group"`
SubGroup string `json:"sub_group"`
}
// ContainsEmoji checks whether given string contains emoji or not. It uses local emoji list as provider.
func ContainsEmoji(s string) bool {
gr := uniseg.NewGraphemes(s)
for gr.Next() {
if _, ok := emojiMap[gr.Str()]; ok {
return true
}
}
return false
}
// AllEmojis gets all emojis from provider.
func AllEmojis() []Emoji {
return emojiMapToSlice(emojiMap)
}
// RemoveEmojis removes all emojis from the s string and returns a new string.
func RemoveEmojis(s string) string {
cleanBuf := bytes.Buffer{}
gr := uniseg.NewGraphemes(s)
for gr.Next() {
if _, ok := emojiMap[gr.Str()]; !ok {
cleanBuf.Write(gr.Bytes())
}
}
return strings.TrimSpace(cleanBuf.String())
}
// GetInfo returns a gomoji.Emoji model representation of provided emoji.
// If the emoji was not found, it returns the gomoji.ErrStrNotEmoji error
func GetInfo(emoji string) (Emoji, error) {
em, ok := emojiMap[emoji]
if !ok {
return Emoji{}, ErrStrNotEmoji
}
return em, nil
}
// FindAll finds all emojis in given string. If there are no emojis it returns a nil-slice.
func FindAll(s string) []Emoji {
var emojis []Emoji
gr := uniseg.NewGraphemes(s)
for gr.Next() {
if em, ok := emojiMap[gr.Str()]; ok {
emojis = append(emojis, em)
}
}
return emojis
}
func emojiMapToSlice(em map[string]Emoji) []Emoji {
emojis := make([]Emoji, 0, len(em))
for _, emoji := range em {
emojis = append(emojis, emoji)
}
return emojis
}

2
vendor/modules.txt vendored
View File

@ -120,6 +120,8 @@ github.com/fjl/memsize
github.com/fjl/memsize/memsizeui
# github.com/flynn/noise v1.0.0
github.com/flynn/noise
# github.com/forPelevin/gomoji v1.1.2 => github.com/status-im/gomoji v1.1.3-0.20220213022530-e5ac4a8732d4
github.com/forPelevin/gomoji
# github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08
github.com/gballet/go-libpcsclite
# github.com/go-ole/go-ole v1.2.5