🏗️ Maked architectural changes

This commit is contained in:
Maxim Lebedev 2018-02-20 20:34:50 +05:00
parent 4aaff889ea
commit a65ca2e796
No known key found for this signature in database
GPG Key ID: F8978F46FF0FFA4F
86 changed files with 1450 additions and 1069 deletions

10
.gitignore vendored
View File

@ -11,13 +11,13 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
# Project-local glide cache
# RE: https://github.com/Masterminds/glide/issues/736
.glide/
# Golang project vendor packages which should be ignored
vendor/
# Production files must not be in public repository
config.yaml
MyPackBot
stickers.db
configs/config.yaml
cert.key
cert.pem

View File

@ -17,10 +17,15 @@ fmt:
# Build localization files with separated untranslated strings
translation:
goi18n merge -format yaml -sourceLanguage en -outdir ./i18n/ ./i18n/*/*
goi18n merge -format yaml \
-sourceLanguage en \
-outdir ./translations/ \
./translations/src/*/*
# Build localization files and merge untranslated strings
localization:
make translation
goi18n -format yaml -sourceLanguage en -outdir ./i18n/ ./i18n/*.all.yaml \
./i18n/*.untranslated.yaml
goi18n -format yaml \
-sourceLanguage en \
-outdir ./translations/ \
./translations/*.all.yaml ./translations/*.untranslated.yaml

View File

@ -1,6 +0,0 @@
# [Patrons!](https://www.patreon.com/bePatron?c=243288)
**These people have sponsored the current version of the project:**
- Daniil Tlenov
- Aurielb
- Yami Odymel
- MoD21k

View File

@ -1,31 +0,0 @@
package main
import (
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
// actions function check Message update on commands, sended stickers or other user stuff
func actions(msg *tg.Message) {
state, err := dbGetUserState(msg.From.ID)
errCheck(err)
log.Ln("state:", state)
switch state {
case stateAddSticker:
actionAdd(msg, false)
case stateAddPack:
actionAdd(msg, true)
case stateDeleteSticker:
actionDelete(msg, false)
case stateDeletePack:
actionDelete(msg, true)
case stateReset:
actionReset(msg)
default:
err = dbChangeUserState(msg.From.ID, stateNone)
errCheck(err)
actionError(msg)
}
}

104
add.go
View File

@ -1,104 +0,0 @@
package main
import (
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
func commandAdd(msg *tg.Message, pack bool) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("reply_add_sticker"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getCancelButton(T)
err = dbChangeUserState(msg.From.ID, stateAddSticker)
errCheck(err)
if pack {
reply.Text = T("reply_add_pack")
err = dbChangeUserState(msg.From.ID, stateAddPack)
errCheck(err)
}
log.Ln("Sending add reply...")
_, err = bot.SendMessage(reply)
errCheck(err)
}
func actionAdd(msg *tg.Message, pack bool) {
if !msg.IsSticker() {
return
}
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("success_add_sticker"))
reply.ParseMode = tg.ModeMarkdown
if !pack {
var exist bool
sticker := msg.Sticker
exist, err = dbAddSticker(
msg.From.ID, sticker.SetName, sticker.FileID, sticker.Emoji,
)
errCheck(err)
if exist {
reply.Text = T("error_already_add_sticker")
}
reply.ReplyMarkup = getCancelButton(T)
_, err = bot.SendMessage(reply)
errCheck(err)
return
}
reply.Text = T("error_empty_add_pack", map[string]interface{}{
"AddStickerCommand": cmdAddSticker,
})
if msg.Sticker.SetName != "" {
var set *tg.StickerSet
set, err = bot.GetStickerSet(msg.Sticker.SetName)
errCheck(err)
log.Ln("SetTitle:", set.Title)
reply.Text = T("success_add_pack", map[string]interface{}{
"SetTitle": set.Title,
})
allExists := true
for _, sticker := range set.Stickers {
var exist bool
exist, err = dbAddSticker(
msg.From.ID, sticker.SetName, sticker.FileID, sticker.Emoji,
)
errCheck(err)
if !exist {
allExists = false
}
}
log.Ln("All exists?", allExists)
if allExists {
reply.Text = T("error_already_add_pack", map[string]interface{}{
"SetTitle": set.Title,
})
}
}
reply.ReplyMarkup = getCancelButton(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}

View File

@ -1,7 +1,6 @@
language: go
go:
- 1.9
- tip
install:

View File

@ -1,39 +0,0 @@
package main
import tg "github.com/toby3d/telegram"
func commandCancel(msg *tg.Message) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
state, err := dbGetUserState(msg.From.ID)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
var text string
switch state {
case stateAddSticker:
text = T("cancel_add_sticker")
case stateAddPack:
text = T("cancel_add_pack")
case stateDeleteSticker:
text = T("cancel_del_sticker")
case stateDeletePack:
text = T("cancel_del_pack")
case stateReset:
text = T("cancel_reset")
default:
text = T("cancel_error")
}
err = dbChangeUserState(msg.From.ID, stateNone)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, text)
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}

View File

@ -1,39 +0,0 @@
package main
import (
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
const (
cmdAddPack = "addPack"
cmdAddSticker = "addSticker"
cmdCancel = "cancel"
cmdHelp = "help"
cmdDeleteSticker = "delSticker"
cmdDeletePack = "delPack"
cmdReset = "reset"
cmdStart = "start"
)
func commands(msg *tg.Message) {
log.Ln("command:", msg.Command())
switch {
case msg.IsCommand(cmdStart):
commandStart(msg)
case msg.IsCommand(cmdHelp):
commandHelp(msg)
case msg.IsCommand(cmdAddSticker):
commandAdd(msg, false)
case msg.IsCommand(cmdAddPack):
commandAdd(msg, true)
case msg.IsCommand(cmdDeleteSticker):
commandDelete(msg, false)
case msg.IsCommand(cmdDeletePack):
commandDelete(msg, true)
case msg.IsCommand(cmdReset):
commandReset(msg)
case msg.IsCommand(cmdCancel):
commandCancel(msg)
}
}

View File

@ -1,7 +1,7 @@
telegram:
token: 123456789:ABCd1efGhjKLM23O4pqR5stuvwx678yz90
webhook:
set: https://www.google.com
set: https://toby3d.github.io
listen: /bot
serve: 0.0.0.0:2368
channel: -1000000000000

View File

@ -1,244 +0,0 @@
package main
import (
"fmt"
"strconv"
"strings"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
)
const (
stateNone = "none"
stateAddSticker = "addSticker"
stateAddPack = "addPack"
stateDeleteSticker = "delSticker"
stateDeletePack = "delPack"
stateReset = "reset"
setUploaded = "?"
)
var db *buntdb.DB
func dbInit() {
log.Ln("Open database file...")
var err error
db, err = buntdb.Open("stickers.db")
errCheck(err)
select {}
}
func dbGetUsers() ([]int, error) {
var users []int
err := db.View(func(tx *buntdb.Tx) error {
return tx.AscendKeys(
"user:*:state",
func(key, val string) bool {
subKeys := strings.Split(key, ":")
id, err := strconv.Atoi(subKeys[1])
if err == nil {
users = append(users, id)
}
return true
},
)
})
if err == buntdb.ErrNotFound {
return nil, nil
}
return users, err
}
func dbChangeUserState(userID int, state string) error {
log.Ln("Trying to change", userID, "state to", state)
return db.Update(func(tx *buntdb.Tx) error {
_, _, err := tx.Set(fmt.Sprint("user:", userID, ":state"), state, nil)
return err
})
}
func dbGetUserState(userID int) (string, error) {
log.Ln("Trying to get", userID, "state")
var state string
err := db.View(func(tx *buntdb.Tx) error {
var err error
state, err = tx.Get(fmt.Sprint("user:", userID, ":state"))
return err
})
switch err {
case buntdb.ErrNotFound:
log.Ln(userID, "not found, create new one")
if err = dbChangeUserState(userID, stateNone); err != nil {
return state, err
}
}
return state, err
}
func dbAddSticker(userID int, setName, fileID, emoji string) (bool, error) {
log.Ln("Trying to add", fileID, "sticker from", userID, "user")
if setName == "" {
setName = setUploaded
}
var exists bool
err := db.Update(func(tx *buntdb.Tx) error {
var err error
_, exists, err = tx.Set(
fmt.Sprint("user:", userID, ":set:", setName, ":sticker:", fileID), // key
emoji, // value
nil, // options
)
if err == buntdb.ErrIndexExists {
exists = true
return nil
}
return err
})
return exists, err
}
func dbDeleteSticker(userID int, setName, fileID string) (bool, error) {
log.Ln("Trying to remove", fileID, "sticker from", userID, "user")
if setName == "" {
setName = setUploaded
}
err := db.Update(func(tx *buntdb.Tx) error {
_, err := tx.Delete(
fmt.Sprint("user:", userID, ":set:", setName, ":sticker:", fileID),
)
return err
})
switch err {
case buntdb.ErrNotFound:
log.Ln(userID, "not found, create new one")
return true, nil
}
return false, err
}
func dbDeletePack(userID int, setName string) (bool, error) {
log.Ln("Trying to remove all", setName, "sticker from", userID, "user")
if setName == "" {
setName = setUploaded
}
var fileIDs []string
err := db.View(func(tx *buntdb.Tx) error {
return tx.AscendKeys(
fmt.Sprint("user:", userID, ":set:", setName, ":*"),
func(key, val string) bool {
keys := strings.Split(key, ":")
fileIDs = append(fileIDs, keys[5])
return true
},
)
})
if len(fileIDs) == 0 {
return true, nil
}
for _, fileID := range fileIDs {
var notExist bool
notExist, err = dbDeleteSticker(userID, setName, fileID)
if err != nil {
return notExist, err
}
}
switch err {
case buntdb.ErrNotFound:
log.Ln(userID, "not found")
return true, nil
}
return false, err
}
func dbResetUserStickers(userID int) error {
log.Ln("Trying reset all stickers of", userID, "user")
return db.Update(func(tx *buntdb.Tx) error {
var keys []string
if err := tx.AscendKeys(
fmt.Sprint("user:", userID, ":set:*"), // index
func(key, val string) bool { // iterator
subKeys := strings.Split(key, ":")
if subKeys[1] == strconv.Itoa(userID) {
keys = append(keys, key)
}
return true
},
); err != nil {
return err
}
for i := range keys {
_, err := tx.Delete(keys[i])
if err != nil {
break
}
}
return nil
})
}
func dbGetUserStickers(userID, offset int, query string) ([]string, int, error) {
log.Ln("Trying to get", userID, "stickers")
var total, count int
var stickers []string
offset = offset * 50
err := db.View(func(tx *buntdb.Tx) error {
return tx.AscendKeys(
fmt.Sprint("user:", userID, ":set:*"), // index
func(key, val string) bool { // iterator
subKeys := strings.Split(key, ":")
if subKeys[1] != strconv.Itoa(userID) {
return true
}
total++
if count >= 51 {
return true
}
if total < offset {
return true
}
if query != "" && !strings.Contains(query, val) {
return true
}
count++
stickers = append(stickers, subKeys[5])
return true
},
)
})
switch {
case err == buntdb.ErrNotFound:
log.Ln("Not found stickers")
return nil, total, nil
case err != nil:
return nil, total, err
}
return stickers, total, nil
}

View File

@ -1,95 +0,0 @@
package main
import (
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
func commandDelete(msg *tg.Message, pack bool) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
_, total, err := dbGetUserStickers(msg.From.ID, 0, "")
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
if total <= 0 {
err = dbChangeUserState(msg.From.ID, stateNone)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("error_empty_del"))
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
return
}
reply := tg.NewMessage(msg.Chat.ID, T("reply_del_sticker"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getCancelButton(T)
err = dbChangeUserState(msg.From.ID, stateDeleteSticker)
errCheck(err)
if pack {
err = dbChangeUserState(msg.From.ID, stateDeletePack)
errCheck(err)
reply.Text = T("reply_del_pack")
}
_, err = bot.SendMessage(reply)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
reply = tg.NewMessage(msg.Chat.ID, T("reply_switch_button"))
reply.ReplyMarkup = getSwitchButton(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}
func actionDelete(msg *tg.Message, pack bool) {
if !msg.IsSticker() {
return
}
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("success_del_sticker"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getCancelButton(T)
var notExist bool
if pack {
var set *tg.StickerSet
set, err = bot.GetStickerSet(msg.Sticker.SetName)
errCheck(err)
log.Ln("SetName:", set.Title)
reply.Text = T("success_del_pack", map[string]interface{}{
"SetTitle": set.Title,
})
notExist, err = dbDeletePack(msg.From.ID, msg.Sticker.SetName)
if notExist {
reply.Text = T("error_already_del_pack")
}
} else {
notExist, err = dbDeleteSticker(msg.From.ID, msg.Sticker.SetName, msg.Sticker.FileID)
if notExist {
reply.Text = T("error_already_del_sticker")
}
}
errCheck(err)
_, err = bot.SendMessage(reply)
errCheck(err)
}

View File

@ -7,7 +7,7 @@ services:
- 2368
volumes:
- ./MyPackBot:/go/MyPackBot
- ./i18n/:/go/i18n/
- ./config.yaml:/go/config.yaml
- ./translations/:/go/translations/
- ./configs/config.yaml:/go/configs/config.yaml
- ./stickers.db:/go/stickers.db
entrypoint: ["/go/MyPackBot", "-webhook"]

39
docs/CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,39 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at git@toby3d.ru. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

43
docs/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,43 @@
# Code standarts
Standards help to keep the code readable and understandable, although they may seem strange or uncomfortable. Code style described below is not strict, but I give priority to those contributors who follow it. :heart:
## Rules
- Format the changes via `go fmt` before committing.
- Indents with tabs (with width 8), no spaces.
- In `import` external packages are separated from native by empty line.
- Maximum line lenght is 120 characters.
- Do not forget to comment what do you do.
- Check what you are writing, tests before commiting.
- Double check what you are writing, remove all [gometalinter](https://github.com/alecthomas/gometalinter) warnings.
## Guidance
Keep in mind the following before, while and after writing the code:
- **Less is always more.**
Write the least amount of code possible to solve just the problem at hand.
- **Predicting the future is impossible.**
Try to distinguish between anticipating potential future problems and potential future features. The former is usually good, the latter is usually bad.
- **Functional programming is functional.**
Functions should be small and single-purpose. Large variable lists are a sign your function does too much.
# Git workflow
`master` contains a stable version of the project, when as `develop` it is constantly updated and contains the latest changes. When proposing changes, you must specify `develop` as the target branch of PR.
## Commits
- First line of commit message should be to 80 chars long as public description of what you have achieved with the commit.
- Leave a blank line after the first line.
- The 3rd line can reference issue with `issue #000` if you just want to mention an issue or `closes #000` if your commit closes an issue. If you don't have an issue to reference or close, think carefully about whether you need to raise one before opening a PR.
- Use bullet points on the following lines to explain what your changes achieve and in particular why you've used your approach.
- Add a contextual emoji before commit title [based on its content](https://gitmoji.carloscuesta.me) (or [use appropriate tool for commiting](https://github.com/carloscuesta/gitmoji-cli)). This **greatly** helps visually to distinguish commits among themselves.
If you need to update your existing commit message, you can do this by running `git commit --amend` on your branch.
## Pull requests
The easier it is for me to merge a PR, the faster we'll be able to do it. Please take steps to make merging easy and keep the history clean and useful.
- **Always work on a branch.**
It will make your life much easier, really. Not touching the `master` branch will also simplify keeping your fork up-to-date.
- **Use issues properly.**
Bugs, changes and features are all different and should be treated differently. Use your commit message to close or reference issues. The more information you provide, the more likely your PR will get merged.
## Issues
Feel free to pick up any issue which is not assigned. Please leave a comment on the issue to say you wish to pick it up, and it will get assigned to you.

20
docs/ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,20 @@
<!--
Welcome to my repo! 👋
Need help or you have a question? Please come chat in Discord: https://discord.gg/KYQB9FR
Found a bug? Want a new feature? Please fill out the sections below. Thanks! 👍
-->
### Summary
<!-- A summary of the issue or, maybe, your ideas for the feature. -->
### How-To
<!--
1. This is the first step
2. This is the second step, etc.
Any other info e.g. Why do you consider this to be a bug? What did you expect to happen instead?
If this is a feature, then describe how it works, what it can potentially be useful, and so on.
P.S.: Bugs are always in priority over the new functionality, unless, of course, it was offered by a sponsor on my Patreon: https://patreon.com/toby3d
-->

View File

@ -0,0 +1,16 @@
<!--
Thanks for your help to my repo! 👋
Need help or you have a question? Please come chat in Discord: https://discord.gg/KYQB9FR
Fixed a bug? Added new feature? Make a new translation? Please fill out the section below. Thanks! 👍
-->
### Summary
<!--
Describe the essence of your commits.
- Is it a fix, feature or a translation?
- What issue is closed by your commits?
- You are totally sure what are you removed all warnings by [gometalinter](https://github.com/alecthomas/gometalinter)?
- Your code is compiled without errors?
- Your code works without obvious errors?
-->

View File

@ -1,8 +1,9 @@
# [@MyPackBot](https://t.me/MyPackBot) [![discord](https://discordapp.com/api/guilds/208605007744860163/widget.png)](https://discord.gg/KYQB9FR)
[![License](https://img.shields.io/crates/l/rustc-serialize.svg)](LICENSE)
[![License](https://img.shields.io/crates/l/rustc-serialize.svg)](docs/LICENSE)
[![Build Status](https://travis-ci.org/toby3d/MyPackBot.svg)](https://travis-ci.org/toby3d/MyPackBot)
[![Go Report](https://goreportcard.com/badge/github.com/toby3d/MyPackBot)](https://goreportcard.com/report/github.com/toby3d/MyPackBot)
[![Release](https://img.shields.io/github/release/toby3d/MyPackBot.svg)](https://github.com/toby3d/MyPackBot/releases/latest)
[![Patreon](https://img.shields.io/badge/support-patreon-E6461A.svg?maxAge=2592000)](https://www.patreon.com/toby3d)
![bot logo](https://raw.githubusercontent.com/toby3d/MyPackBot/gh-pages/static/social/og-image.jpg)
@ -16,7 +17,7 @@ This is a Telegram-bot that collects all the stickers sent to it in one (almost)
- Keeps stickers belonging to their original sets;
- Fully support the standard functionality of Telegram stickers (for example "add to favorites");
- Avaliable anywhere in Telegram by typing `@MyPackBot ` in the input field;
- Supports filtering of results by emoji: `@MyPackBot 😀`;
- Supports filtering of results by emoji's: `@MyPackBot 😀👍`;
- Fast as f\*\*\*king Sonic;
- Worked with uploadable WebP stickers;
- Worked with blocked by rightholders sets (but this is not exact);
@ -66,7 +67,7 @@ Bot uses the following dependencies:
## Support
### GitHub
You can [request fix/add some things](https://github.com/toby3d/MyPackBot/issues/new), [make a patch](https://github.com/toby3d/MyPackBot/compare) or help with [translation and localization](https://github.com/toby3d/MyPackBot/tree/develop/i18n) on your language.
You can [request fix/add some things](https://github.com/toby3d/MyPackBot/issues/new), [make a patch](https://github.com/toby3d/MyPackBot/compare) or help with [translation and localization](https://github.com/toby3d/MyPackBot/tree/develop/translations) on your language.
Ah, and star this repo, of course.

8
docs/SUPPORT.md Normal file
View File

@ -0,0 +1,8 @@
# [Support me on Patreon!](https://www.patreon.com/bePatron?c=243288)
I develop this project in my spare time, and I do it and I will do it free of charge. However, you can make a donation or become a sponsor to make sure that I have enough coffee and pizza for night coding.
**These people sponsored current version of the project:**
- Yami Odymel
- Daniil Tlenov
- Aurielb
- MoD21k

View File

@ -1,12 +0,0 @@
package main
import "fmt"
// errCheck helps debug critical errors without warnings from 'gocyclo' linter
func errCheck(err error) {
if err != nil {
fmt.Sprintln(err.Error())
waitForwards.Wait() // Wait what all users get announcement message
panic(err.Error())
}
}

View File

@ -1,25 +0,0 @@
package main
import tg "github.com/toby3d/telegram"
func actionError(msg *tg.Message) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
reply := tg.NewMessage(
msg.Chat.ID, T("error_unknown", map[string]interface{}{
"AddStickerCommand": cmdAddSticker,
"AddPackCommand": cmdAddPack,
"DeleteStickerCommand": cmdDeleteSticker,
"DeletePackCommand": cmdDeletePack,
}),
)
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}

View File

@ -1,57 +0,0 @@
package main
import (
"fmt"
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
// allowedUpdates is a value for parameter of updates configuration
var allowedUpdates = []string{
tg.UpdateInlineQuery, // For searching and sending stickers
tg.UpdateMessage, // For get commands and messages
tg.UpdateChannelPost,
}
// getUpdatesChannel return webhook or long polling channel with bot updates
func getUpdatesChannel() tg.UpdatesChannel {
log.Ln("Preparing channel for updates...")
if !*flagWebhook {
log.Ln("Use LongPolling updates")
log.Ln("Deleting webhook if exists")
_, err := bot.DeleteWebhook()
errCheck(err)
return bot.NewLongPollingChannel(&tg.GetUpdatesParameters{
Offset: 0,
Limit: 100,
Timeout: 60,
AllowedUpdates: allowedUpdates,
})
}
set := cfg.UString("telegram.webhook.set")
listen := cfg.UString("telegram.webhook.listen")
serve := cfg.UString("telegram.webhook.serve")
log.Ln(
"Trying set webhook on address:",
fmt.Sprint(set, listen, bot.AccessToken),
)
log.Ln("Creating new webhook...")
webhook := tg.NewWebhook(fmt.Sprint(set, listen, bot.AccessToken), nil)
webhook.MaxConnections = 40
webhook.AllowedUpdates = allowedUpdates
return bot.NewWebhookChannel(
webhook, // params
"", // certFile
"", // keyFile
set, // set
fmt.Sprint(listen, bot.AccessToken), // listen
serve, // serve
)
}

View File

@ -0,0 +1,4 @@
#!/bin/sh
# gitmoji as a commit hook
exec < /dev/tty
gitmoji --hook $1

31
help.go
View File

@ -1,31 +0,0 @@
package main
import tg "github.com/toby3d/telegram"
func commandHelp(msg *tg.Message) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
err = dbChangeUserState(msg.From.ID, stateNone)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
reply := tg.NewMessage(
msg.Chat.ID, T("reply_help", map[string]interface{}{
"AddStickerCommand": cmdAddSticker,
"AddPackCommand": cmdAddPack,
"DeleteStickerCommand": cmdDeleteSticker,
"DeletePackCommand": cmdDeletePack,
"ResetCommand": cmdReset,
"CancelCommand": cmdCancel,
"Username": bot.Self.Username,
}),
)
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}

View File

@ -1,58 +0,0 @@
package main
import (
"github.com/nicksnyder/go-i18n/i18n"
tg "github.com/toby3d/telegram"
"golang.org/x/text/runes"
"golang.org/x/text/transform"
)
var bannedSkins = []rune{127995, 127996, 127997, 127998, 127999}
var skinRemover = runes.Remove(runes.Predicate(
func(r rune) bool {
for _, skin := range bannedSkins {
if r == skin {
return true
}
}
return false
},
))
func getMenuKeyboard(T i18n.TranslateFunc) *tg.ReplyKeyboardMarkup {
return tg.NewReplyKeyboardMarkup(
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_add_sticker")),
tg.NewReplyKeyboardButton(T("button_add_pack")),
),
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_del_sticker")),
tg.NewReplyKeyboardButton(T("button_del_pack")),
),
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_reset")),
),
)
}
func getCancelButton(T i18n.TranslateFunc) *tg.ReplyKeyboardMarkup {
return tg.NewReplyKeyboardMarkup(
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_cancel")),
),
)
}
func getSwitchButton(T i18n.TranslateFunc) *tg.InlineKeyboardMarkup {
return tg.NewInlineKeyboardMarkup(
tg.NewInlineKeyboardRow(
tg.NewInlineKeyboardButtonSwitchSelf(T("button_inline_select"), " "),
),
)
}
func fixEmoji(raw string) (string, error) {
result, _, err := transform.String(skinRemover, raw)
return result, err
}

66
init.go
View File

@ -1,66 +0,0 @@
package main
import (
"flag"
"os"
"path/filepath"
"strings"
log "github.com/kirillDanshin/dlog"
"github.com/nicksnyder/go-i18n/i18n"
"github.com/olebedev/config"
)
var (
// Variables with types from imports
cfg *config.Config
channelID int64
// Setted variables
flagWebhook = flag.Bool(
"webhook",
false,
"enable work via webhooks (required valid certificates)",
)
)
// init prepare configuration and other things for successful start of main
// function.
func init() {
log.Ln("Initializing...")
log.Ln("Parse flags...")
flag.Parse()
err := filepath.Walk("./i18n/", func(path string, info os.FileInfo, err error) error {
if strings.HasSuffix(path, ".all.yaml") {
i18n.MustLoadTranslationFile(path)
}
return nil
})
errCheck(err)
log.Ln("Loading configuration file...")
cfg, err = config.ParseYamlFile("config.yaml")
errCheck(err)
log.Ln("Checking bot access token in configuration file...")
_, err = cfg.String("telegram.token")
errCheck(err)
if *flagWebhook {
log.Ln("Enabled webhook mode, check configuration strings...")
log.Ln("Checking webhook set string...")
_, err = cfg.String("telegram.webhook.set")
errCheck(err)
log.Ln("Checking webhook listen string...")
_, err = cfg.String("telegram.webhook.listen")
errCheck(err)
log.Ln("Checking webhook listen string...")
_, err = cfg.String("telegram.webhook.serve")
errCheck(err)
}
channelID = int64(cfg.UInt("telegram.channel"))
}

18
init/init.go Normal file
View File

@ -0,0 +1,18 @@
package init
import (
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/config"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/i18n"
)
// init prepare configuration and other things for successful start
func init() {
log.Ln("Initializing...")
i18n.Open("translations/") // Preload localization strings
config.Open("configs/config.yaml") // Preload configuration file
db.Open("stickers.db") // Open database or create new one
bot.New() // Create bot with credentials from config
}

35
internal/actions/action.go Executable file
View File

@ -0,0 +1,35 @@
package actions
import (
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Action function check Message update on commands, sended stickers or other
// user stuff if user state is not 'none'
func Action(msg *tg.Message) {
state, err := db.UserState(msg.From.ID)
errors.Check(err)
log.Ln("state:", state)
switch state {
case models.StateAddSticker:
Add(msg, false)
case models.StateAddPack:
Add(msg, true)
case models.StateDeleteSticker:
Delete(msg, false)
case models.StateDeletePack:
Delete(msg, true)
case models.StateReset:
Reset(msg)
default:
err = db.ChangeUserState(msg.From.ID, models.StateNone)
errors.Check(err)
Error(msg)
}
}

85
internal/actions/add.go Normal file
View File

@ -0,0 +1,85 @@
package actions
import (
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Add action add sticker or set to user's pack
func Add(msg *tg.Message, pack bool) {
if !msg.IsSticker() {
return
}
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, T("success_add_sticker"))
reply.ParseMode = tg.ModeMarkdown
if !pack {
var exist bool
sticker := msg.Sticker
exist, err = db.AddSticker(
msg.From.ID, sticker.SetName, sticker.FileID, sticker.Emoji,
)
errors.Check(err)
if exist {
reply.Text = T("error_already_add_sticker")
}
reply.ReplyMarkup = helpers.CancelButton(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
return
}
reply.Text = T("error_empty_add_pack", map[string]interface{}{
"AddStickerCommand": models.CommandAddSticker,
})
if msg.Sticker.SetName != "" {
var set *tg.StickerSet
set, err = bot.Bot.GetStickerSet(msg.Sticker.SetName)
errors.Check(err)
log.Ln("SetTitle:", set.Title)
reply.Text = T("success_add_pack", map[string]interface{}{
"SetTitle": set.Title,
})
allExists := true
for _, sticker := range set.Stickers {
var exist bool
exist, err = db.AddSticker(
msg.From.ID, sticker.SetName, sticker.FileID, sticker.Emoji,
)
errors.Check(err)
if !exist {
allExists = false
}
}
log.Ln("All exists?", allExists)
if allExists {
reply.Text = T("error_already_add_pack", map[string]interface{}{
"SetTitle": set.Title,
})
}
}
reply.ReplyMarkup = helpers.CancelButton(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

View File

@ -0,0 +1,58 @@
package actions
import (
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
tg "github.com/toby3d/telegram"
)
// Delete action remove sticker or set from user's pack
func Delete(msg *tg.Message, pack bool) {
if !msg.IsSticker() {
return
}
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, T("success_del_sticker"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.CancelButton(T)
var notExist bool
if pack {
var set *tg.StickerSet
set, err = bot.Bot.GetStickerSet(msg.Sticker.SetName)
errors.Check(err)
log.Ln("SetName:", set.Title)
reply.Text = T("success_del_pack", map[string]interface{}{
"SetTitle": set.Title,
})
notExist, err = db.DeletePack(msg.From.ID, msg.Sticker.SetName)
if notExist {
reply.Text = T("error_already_del_pack")
}
} else {
notExist, err = db.DeleteSticker(
msg.From.ID,
msg.Sticker.SetName,
msg.Sticker.FileID,
)
if notExist {
reply.Text = T("error_already_del_sticker")
}
}
errors.Check(err)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

33
internal/actions/error.go Normal file
View File

@ -0,0 +1,33 @@
package actions
import (
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Error action send error reply about invalid user request
func Error(msg *tg.Message) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
reply := tg.NewMessage(
msg.Chat.ID, T("error_unknown", map[string]interface{}{
"AddStickerCommand": models.CommandAddSticker,
"AddPackCommand": models.CommandAddPack,
"DeleteStickerCommand": models.CommandDeleteSticker,
"DeletePackCommand": models.CommandDeletePack,
}),
)
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

44
internal/actions/reset.go Normal file
View File

@ -0,0 +1,44 @@
package actions
import (
"strings"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Reset action checks key phrase and reset user's pack
func Reset(msg *tg.Message) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
err = db.ChangeUserState(msg.From.ID, models.StateNone)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
if !strings.EqualFold(msg.Text, T("key_phrase")) {
reply := tg.NewMessage(msg.Chat.ID, T("error_reset_phrase"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
return
}
err = db.ResetUser(msg.From.ID)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, T("success_reset"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

18
internal/bot/new.go Normal file
View File

@ -0,0 +1,18 @@
package bot
import (
"github.com/toby3d/MyPackBot/internal/config"
"github.com/toby3d/MyPackBot/internal/errors"
tg "github.com/toby3d/telegram"
)
var Bot *tg.Bot
// New just create new bot by configuration credentials
func New() {
accessToken, err := config.Config.String("telegram.token")
errors.Check(err)
Bot, err = tg.NewBot(accessToken)
errors.Check(err)
}

39
internal/commands/add.go Normal file
View File

@ -0,0 +1,39 @@
package commands
import (
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Add command prepare user for adding some stickers or sets to his pack
func Add(msg *tg.Message, pack bool) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, T("reply_add_sticker"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.CancelButton(T)
err = db.ChangeUserState(msg.From.ID, models.StateAddSticker)
errors.Check(err)
if pack {
reply.Text = T("reply_add_pack")
err = db.ChangeUserState(msg.From.ID, models.StateAddPack)
errors.Check(err)
}
log.Ln("Sending add reply...")
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

48
internal/commands/cancel.go Executable file
View File

@ -0,0 +1,48 @@
package commands
import (
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Cancel just cancel current user operation
func Cancel(msg *tg.Message) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
state, err := db.UserState(msg.From.ID)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
var text string
switch state {
case models.StateAddSticker:
text = T("cancel_add_sticker")
case models.StateAddPack:
text = T("cancel_add_pack")
case models.StateDeleteSticker:
text = T("cancel_del_sticker")
case models.StateDeletePack:
text = T("cancel_del_pack")
case models.StateReset:
text = T("cancel_reset")
default:
text = T("cancel_error")
}
err = db.ChangeUserState(msg.From.ID, models.StateNone)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, text)
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

30
internal/commands/command.go Executable file
View File

@ -0,0 +1,30 @@
package commands
import (
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Command check's got user command
func Command(msg *tg.Message) {
log.Ln("command:", msg.Command())
switch {
case msg.IsCommand(models.CommandStart):
Start(msg)
case msg.IsCommand(models.CommandHelp):
Help(msg)
case msg.IsCommand(models.CommandAddSticker):
Add(msg, false)
case msg.IsCommand(models.CommandAddPack):
Add(msg, true)
case msg.IsCommand(models.CommandDeleteSticker):
Delete(msg, false)
case msg.IsCommand(models.CommandDeletePack):
Delete(msg, true)
case msg.IsCommand(models.CommandReset):
Reset(msg)
case msg.IsCommand(models.CommandCancel):
Cancel(msg)
}
}

59
internal/commands/delete.go Executable file
View File

@ -0,0 +1,59 @@
package commands
import (
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Delete prepare user to remove some stickers or sets from his pack
func Delete(msg *tg.Message, pack bool) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
_, total, err := db.UserStickers(msg.From.ID, 0, "")
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
if total <= 0 {
err = db.ChangeUserState(msg.From.ID, models.StateNone)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, T("error_empty_del"))
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
return
}
reply := tg.NewMessage(msg.Chat.ID, T("reply_del_sticker"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.CancelButton(T)
err = db.ChangeUserState(msg.From.ID, models.StateDeleteSticker)
errors.Check(err)
if pack {
err = db.ChangeUserState(msg.From.ID, models.StateDeletePack)
errors.Check(err)
reply.Text = T("reply_del_pack")
}
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
reply = tg.NewMessage(msg.Chat.ID, T("reply_switch_button"))
reply.ReplyMarkup = helpers.SwitchButton(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

40
internal/commands/help.go Executable file
View File

@ -0,0 +1,40 @@
package commands
import (
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Help just send instructions about bot usage
func Help(msg *tg.Message) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
err = db.ChangeUserState(msg.From.ID, models.StateNone)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
reply := tg.NewMessage(
msg.Chat.ID, T("reply_help", map[string]interface{}{
"AddStickerCommand": models.CommandAddSticker,
"AddPackCommand": models.CommandAddPack,
"DeleteStickerCommand": models.CommandDeleteSticker,
"DeletePackCommand": models.CommandDeletePack,
"ResetCommand": models.CommandReset,
"CancelCommand": models.CommandCancel,
"Username": bot.Bot.Self.Username,
}),
)
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

View File

@ -0,0 +1,47 @@
package commands
import (
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Reset prepare user to reset his pack
func Reset(msg *tg.Message) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
_, total, err := db.UserStickers(msg.From.ID, 0, "")
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
if total <= 0 {
err = db.ChangeUserState(msg.From.ID, models.StateNone)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, T("error_already_reset"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
return
}
err = db.ChangeUserState(msg.From.ID, models.StateReset)
errors.Check(err)
reply := tg.NewMessage(msg.Chat.ID, T("reply_reset", map[string]interface{}{
"KeyPhrase": T("key_phrase"),
"CancelCommand": models.CommandCancel,
}))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.CancelButton(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

View File

@ -0,0 +1,47 @@
package commands
import (
"strings"
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Start just send introduction about bot to user
func Start(msg *tg.Message) {
err := db.ChangeUserState(msg.From.ID, models.StateNone)
errors.Check(err)
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errors.Check(err)
if msg.HasCommandArgument() {
log.Ln("Received a", msg.Command(), "command with", msg.CommandArgument(), "argument")
if strings.EqualFold(msg.CommandArgument(), models.CommandHelp) {
Help(msg)
return
}
}
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
reply := tg.NewMessage(
msg.Chat.ID,
T("reply_start", map[string]interface{}{
"Username": bot.Bot.Self.Username,
"ID": bot.Bot.Self.ID,
}),
)
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = helpers.MenuKeyboard(T)
_, err = bot.Bot.SendMessage(reply)
errors.Check(err)
}

20
internal/config/open.go Normal file
View File

@ -0,0 +1,20 @@
package config
import (
"github.com/olebedev/config"
"github.com/toby3d/MyPackBot/internal/errors"
)
var (
Config *config.Config
ChannelID int64
)
// Open just open configuration file for parsing some data in other functions
func Open(path string) {
var err error
Config, err = config.ParseYamlFile(path)
errors.Check(err)
ChannelID = int64(Config.UInt("telegram.channel"))
}

View File

@ -0,0 +1,35 @@
package db
import (
"fmt"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
"github.com/toby3d/MyPackBot/internal/models"
)
// AddSticker add sticker FileID, Emoji and SetName meta for UserID
func AddSticker(userID int, setName, fileID, emoji string) (bool, error) {
log.Ln("Trying to add", fileID, "sticker from", userID, "user")
if setName == "" {
setName = models.SetUploaded
}
var exists bool
err := DB.Update(func(tx *buntdb.Tx) error {
var err error
_, exists, err = tx.Set(
fmt.Sprint("user:", userID, ":set:", setName, ":sticker:", fileID), // key
emoji, // value
nil, // options
)
if err == buntdb.ErrIndexExists {
exists = true
return nil
}
return err
})
return exists, err
}

View File

@ -0,0 +1,17 @@
package db
import (
"fmt"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
)
// ChangeUserState change current user state on input state.
func ChangeUserState(userID int, state string) error {
log.Ln("Trying to change", userID, "state to", state)
return DB.Update(func(tx *buntdb.Tx) error {
_, _, err := tx.Set(fmt.Sprint("user:", userID, ":state"), state, nil)
return err
})
}

View File

@ -0,0 +1,50 @@
package db
import (
"fmt"
"strings"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
"github.com/toby3d/MyPackBot/internal/models"
)
// DeletePack remove all keys for UserID which contains input SetName
func DeletePack(userID int, setName string) (bool, error) {
log.Ln("Trying to remove all", setName, "sticker from", userID, "user")
if setName == "" {
setName = models.SetUploaded
}
var fileIDs []string
err := DB.View(func(tx *buntdb.Tx) error {
return tx.AscendKeys(
fmt.Sprint("user:", userID, ":set:", setName, ":*"),
func(key, val string) bool {
keys := strings.Split(key, ":")
fileIDs = append(fileIDs, keys[5])
return true
},
)
})
if len(fileIDs) == 0 {
return true, nil
}
for _, fileID := range fileIDs {
var notExist bool
notExist, err = DeleteSticker(userID, setName, fileID)
if err != nil {
return notExist, err
}
}
switch err {
case buntdb.ErrNotFound:
log.Ln(userID, "not found")
return true, nil
}
return false, err
}

View File

@ -0,0 +1,31 @@
package db
import (
"fmt"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
"github.com/toby3d/MyPackBot/internal/models"
)
// DeleteSticker just remove specified sticker key from database.
func DeleteSticker(userID int, setName, fileID string) (bool, error) {
log.Ln("Trying to remove", fileID, "sticker from", userID, "user")
if setName == "" {
setName = models.SetUploaded
}
err := DB.Update(func(tx *buntdb.Tx) error {
_, err := tx.Delete(
fmt.Sprint("user:", userID, ":set:", setName, ":sticker:", fileID),
)
return err
})
if err == buntdb.ErrNotFound {
log.Ln(userID, "not found, create new one")
return true, nil
}
return false, err
}

19
internal/db/open.go Normal file
View File

@ -0,0 +1,19 @@
package db
import (
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
"github.com/toby3d/MyPackBot/internal/errors"
)
var DB *buntdb.DB
// Open just open connection to database for work
func Open(path string) {
log.Ln("Open database file...")
go func() {
var err error
DB, err = buntdb.Open(path)
errors.Check(err)
}()
}

39
internal/db/reset.go Normal file
View File

@ -0,0 +1,39 @@
package db
import (
"fmt"
"strconv"
"strings"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
)
// ResetUser just drop out all stickers keys for input UserID
func ResetUser(userID int) error {
log.Ln("Trying reset all stickers of", userID, "user")
return DB.Update(func(tx *buntdb.Tx) error {
var keys []string
if err := tx.AscendKeys(
fmt.Sprint("user:", userID, ":set:*"), // index
func(key, val string) bool { // iterator
subKeys := strings.Split(key, ":")
if subKeys[1] == strconv.Itoa(userID) {
keys = append(keys, key)
}
return true
},
); err != nil {
return err
}
for i := range keys {
_, err := tx.Delete(keys[i])
if err != nil {
break
}
}
return nil
})
}

30
internal/db/user_state.go Normal file
View File

@ -0,0 +1,30 @@
package db
import (
"fmt"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
"github.com/toby3d/MyPackBot/internal/models"
)
// UserState return current state for UserID
func UserState(userID int) (string, error) {
log.Ln("Trying to get", userID, "state")
var state string
err := DB.View(func(tx *buntdb.Tx) error {
var err error
state, err = tx.Get(fmt.Sprint("user:", userID, ":state"))
return err
})
switch err {
case buntdb.ErrNotFound:
log.Ln(userID, "not found, create new one")
if err = ChangeUserState(userID, models.StateNone); err != nil {
return state, err
}
}
return state, err
}

View File

@ -0,0 +1,54 @@
package db
import (
"fmt"
"strconv"
"strings"
log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
)
// UserStickers return array of saved stickers for input UserID and his total count
func UserStickers(userID, offset int, query string) ([]string, int, error) {
log.Ln("Trying to get", userID, "stickers")
var total, count int
var stickers []string
offset = offset * 50
err := DB.View(func(tx *buntdb.Tx) error {
return tx.AscendKeys(
fmt.Sprint("user:", userID, ":set:*"), // index
func(key, val string) bool { // iterator
subKeys := strings.Split(key, ":")
if subKeys[1] != strconv.Itoa(userID) {
return true
}
total++
if count >= 51 {
return true
}
if total < offset {
return true
}
if query != "" && !strings.Contains(query, val) {
return true
}
count++
stickers = append(stickers, subKeys[5])
return true
},
)
})
if err == buntdb.ErrNotFound {
log.Ln("Not found stickers")
return nil, total, nil
}
return stickers, total, err
}

33
internal/db/users.go Normal file
View File

@ -0,0 +1,33 @@
package db
import (
"strconv"
"strings"
// log "github.com/kirillDanshin/dlog"
"github.com/tidwall/buntdb"
)
// Users return array of all avaliable UserID in database
func Users() ([]int, error) {
var users []int
err := DB.View(func(tx *buntdb.Tx) error {
return tx.AscendKeys(
"user:*:state",
func(key, val string) bool {
subKeys := strings.Split(key, ":")
id, err := strconv.Atoi(subKeys[1])
if err == nil {
users = append(users, id)
}
return true
},
)
})
if err == buntdb.ErrNotFound {
return nil, nil
}
return users, err
}

26
internal/errors/check.go Normal file
View File

@ -0,0 +1,26 @@
package errors
import (
"log"
"log/syslog"
"os"
"sync"
)
var (
WaitForwards = new(sync.WaitGroup)
sysLogger *syslog.Writer
)
// Check helps debug critical errors without warnings from 'gocyclo' linter
func Check(err error) {
if err != nil {
log.Println(err.Error())
// Wait what all users get announcement message first
WaitForwards.Wait()
sysLogger.Crit(err.Error())
os.Exit(1)
}
}

View File

@ -0,0 +1,15 @@
package helpers
import (
"github.com/nicksnyder/go-i18n/i18n"
tg "github.com/toby3d/telegram"
)
// CancelButton helper just generate ReplyMarkup with cancel button
func CancelButton(T i18n.TranslateFunc) *tg.ReplyKeyboardMarkup {
return tg.NewReplyKeyboardMarkup(
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_cancel")),
),
)
}

View File

@ -0,0 +1,27 @@
package helpers
import (
"golang.org/x/text/runes"
"golang.org/x/text/transform"
)
// Skin colors for remove
var bannedSkins = []rune{127995, 127996, 127997, 127998, 127999}
// Transformer for remove skin colors
var skinRemover = runes.Remove(runes.Predicate(
func(r rune) bool {
for _, skin := range bannedSkins {
if r == skin {
return true
}
}
return false
},
))
// FixEmoji fixes user input by remove all potential skin colors
func FixEmoji(raw string) (string, error) {
result, _, err := transform.String(skinRemover, raw)
return result, err
}

View File

@ -0,0 +1,23 @@
package helpers
import (
"github.com/nicksnyder/go-i18n/i18n"
tg "github.com/toby3d/telegram"
)
// MenuKeyboard helper just generate ReplyMarkup with menu buttons
func MenuKeyboard(T i18n.TranslateFunc) *tg.ReplyKeyboardMarkup {
return tg.NewReplyKeyboardMarkup(
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_add_sticker")),
tg.NewReplyKeyboardButton(T("button_add_pack")),
),
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_del_sticker")),
tg.NewReplyKeyboardButton(T("button_del_pack")),
),
tg.NewReplyKeyboardRow(
tg.NewReplyKeyboardButton(T("button_reset")),
),
)
}

View File

@ -0,0 +1,15 @@
package helpers
import (
"github.com/nicksnyder/go-i18n/i18n"
tg "github.com/toby3d/telegram"
)
// SwitchButton helper just generate ReplyMarkup with SelfSwitch button
func SwitchButton(T i18n.TranslateFunc) *tg.InlineKeyboardMarkup {
return tg.NewInlineKeyboardMarkup(
tg.NewInlineKeyboardRow(
tg.NewInlineKeyboardButtonSwitchSelf(T("button_inline_select"), " "),
),
)
}

20
internal/i18n/open.go Normal file
View File

@ -0,0 +1,20 @@
package i18n
import (
"os"
"path/filepath"
"strings"
"github.com/nicksnyder/go-i18n/i18n"
)
// Open just walk in input path for preloading localization files
func Open(path string) error {
return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if !strings.HasSuffix(path, ".all.yaml") {
return err
}
return i18n.LoadTranslationFile(path)
})
}

View File

@ -0,0 +1,13 @@
package i18n
import (
"github.com/nicksnyder/go-i18n/i18n"
"github.com/toby3d/MyPackBot/internal/models"
)
// SwitchTo try load locales by input language codes and return TranslateFunc
func SwitchTo(codes ...string) (T i18n.TranslateFunc, err error) {
codes = append(codes, models.LanguageFallback)
T, err = i18n.Tfunc(codes[0], codes[1:]...)
return
}

View File

@ -0,0 +1,35 @@
package messages
import (
"strings"
"github.com/toby3d/MyPackBot/internal/actions"
"github.com/toby3d/MyPackBot/internal/commands"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/i18n"
tg "github.com/toby3d/telegram"
)
// Message checks user message on response, stickers, reset key phrase, else do
// Actions
func Message(msg *tg.Message) {
T, err := i18n.SwitchTo(msg.From.LanguageCode)
errors.Check(err)
switch {
case strings.EqualFold(msg.Text, T("button_add_sticker")):
commands.Add(msg, false)
case strings.EqualFold(msg.Text, T("button_add_pack")):
commands.Add(msg, true)
case strings.EqualFold(msg.Text, T("button_del_sticker")):
commands.Delete(msg, false)
case strings.EqualFold(msg.Text, T("button_del_pack")):
commands.Delete(msg, true)
case strings.EqualFold(msg.Text, T("button_reset")):
commands.Reset(msg)
case strings.EqualFold(msg.Text, T("button_cancel")):
commands.Cancel(msg)
default:
actions.Action(msg)
}
}

31
internal/models/models.go Normal file
View File

@ -0,0 +1,31 @@
package models
import tg "github.com/toby3d/telegram"
const (
CommandAddPack = "addPack"
CommandAddSticker = "addSticker"
CommandCancel = "cancel"
CommandHelp = "help"
CommandDeleteSticker = "delSticker"
CommandDeletePack = "delPack"
CommandReset = "reset"
CommandStart = "start"
StateNone = "none"
StateAddSticker = CommandAddSticker
StateAddPack = CommandAddPack
StateDeleteSticker = CommandDeleteSticker
StateDeletePack = CommandDeletePack
StateReset = CommandReset
SetUploaded = "?"
LanguageFallback = "en"
)
var AllowedUpdates = []string{
tg.UpdateInlineQuery, // For searching and sending stickers
tg.UpdateMessage, // For get commands and messages
tg.UpdateChannelPost, // For forwarding announcements
}

View File

@ -0,0 +1,64 @@
package updates
import (
"fmt"
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/config"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
// Channel return webhook or long polling channel with bot updates
func Channel(webhookMode bool) tg.UpdatesChannel {
log.Ln("Preparing channel for updates...")
if !webhookMode {
log.Ln("Use LongPolling updates")
info, err := bot.Bot.GetWebhookInfo()
errors.Check(err)
if info.URL != "" {
log.Ln("Deleting webhook...")
_, err := bot.Bot.DeleteWebhook()
errors.Check(err)
}
return bot.Bot.NewLongPollingChannel(&tg.GetUpdatesParameters{
Offset: 0,
Limit: 100,
Timeout: 60,
AllowedUpdates: models.AllowedUpdates,
})
}
var err error
var set, listen, serve string
set, err = config.Config.String("telegram.webhook.set")
errors.Check(err)
listen, err = config.Config.String("telegram.webhook.listen")
errors.Check(err)
serve, err = config.Config.String("telegram.webhook.serve")
errors.Check(err)
log.Ln(
"Trying set webhook on address:",
fmt.Sprint(set, listen, bot.Bot.AccessToken),
)
log.Ln("Creating new webhook...")
webhook := tg.NewWebhook(fmt.Sprint(set, listen, bot.Bot.AccessToken), nil)
webhook.MaxConnections = 40
webhook.AllowedUpdates = models.AllowedUpdates
return bot.Bot.NewWebhookChannel(
webhook, // params
"", // certFile
"", // keyFile
set, // set
fmt.Sprint(listen, bot.Bot.AccessToken), // listen
serve, // serve
)
}

View File

@ -0,0 +1,35 @@
package updates
import (
"time"
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/config"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
tg "github.com/toby3d/telegram"
)
// ChannelPost checks ChannelPost update for forwarding content to bot users
func ChannelPost(post *tg.Message) {
if post.Chat.ID != config.ChannelID {
log.Ln(post.Chat.ID, "!=", config.ChannelID)
return
}
users, err := db.Users()
errors.Check(err)
for i := range users {
errors.WaitForwards.Add(1)
if _, err = bot.Bot.ForwardMessage(
tg.NewForwardMessage(post.Chat.ID, int64(users[i]), post.ID),
); err != nil {
log.Ln(err.Error())
}
errors.WaitForwards.Done()
time.Sleep(time.Second / 10) // For avoid spamming
}
}

View File

@ -1,14 +1,21 @@
package main
package updates
import (
"strconv"
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/db"
"github.com/toby3d/MyPackBot/internal/errors"
"github.com/toby3d/MyPackBot/internal/helpers"
"github.com/toby3d/MyPackBot/internal/i18n"
"github.com/toby3d/MyPackBot/internal/models"
tg "github.com/toby3d/telegram"
)
func updateInlineQuery(inlineQuery *tg.InlineQuery) {
fixedQuery, err := fixEmoji(inlineQuery.Query)
// InlineQuery checks InlineQuery updates for answer with personal results
func InlineQuery(inlineQuery *tg.InlineQuery) {
fixedQuery, err := helpers.FixEmoji(inlineQuery.Query)
if err == nil {
inlineQuery.Query = fixedQuery
}
@ -19,27 +26,27 @@ func updateInlineQuery(inlineQuery *tg.InlineQuery) {
answer.IsPersonal = true
if len([]rune(inlineQuery.Query)) >= 256 {
_, err = bot.AnswerInlineQuery(answer)
errCheck(err)
_, err = bot.Bot.AnswerInlineQuery(answer)
errors.Check(err)
return
}
log.Ln("Let's preparing answer...")
T, err := switchLocale(inlineQuery.From.LanguageCode)
errCheck(err)
T, err := i18n.SwitchTo(inlineQuery.From.LanguageCode)
errors.Check(err)
log.Ln("INLINE OFFSET:", inlineQuery.Offset)
if inlineQuery.Offset == "" {
inlineQuery.Offset = "-1"
}
offset, err := strconv.Atoi(inlineQuery.Offset)
errCheck(err)
errors.Check(err)
offset++
stickers, packSize, err := dbGetUserStickers(
stickers, packSize, err := db.UserStickers(
inlineQuery.From.ID, offset, inlineQuery.Query,
)
errCheck(err)
errors.Check(err)
totalStickers := len(stickers)
if totalStickers == 0 {
@ -51,11 +58,11 @@ func updateInlineQuery(inlineQuery *tg.InlineQuery) {
"Query": inlineQuery.Query,
},
)
answer.SwitchPrivateMessageParameter = cmdAddSticker
answer.SwitchPrivateMessageParameter = models.CommandAddSticker
} else {
// If query is empty and get 0 stickers
answer.SwitchPrivateMessageText = T("button_inline_empty")
answer.SwitchPrivateMessageParameter = cmdAddSticker
answer.SwitchPrivateMessageParameter = models.CommandAddSticker
}
answer.Results = nil
}
@ -80,12 +87,12 @@ func updateInlineQuery(inlineQuery *tg.InlineQuery) {
"Count": packSize,
},
)
answer.SwitchPrivateMessageParameter = cmdHelp
answer.SwitchPrivateMessageParameter = models.CommandHelp
answer.Results = results
}
log.Ln("CacheTime:", answer.CacheTime)
_, err = bot.AnswerInlineQuery(answer)
errCheck(err)
_, err = bot.Bot.AnswerInlineQuery(answer)
errors.Check(err)
}

View File

@ -0,0 +1,29 @@
package updates
import (
log "github.com/kirillDanshin/dlog"
"github.com/toby3d/MyPackBot/internal/actions"
"github.com/toby3d/MyPackBot/internal/bot"
"github.com/toby3d/MyPackBot/internal/commands"
"github.com/toby3d/MyPackBot/internal/messages"
tg "github.com/toby3d/telegram"
)
// Message checks Message updates for answer to user commands, replies or sended
// stickers
func Message(msg *tg.Message) {
if bot.Bot.IsMessageFromMe(msg) ||
bot.Bot.IsForwardFromMe(msg) {
log.Ln("Ignore message update")
return
}
switch {
case bot.Bot.IsCommandToMe(msg):
commands.Command(msg)
case msg.IsText():
messages.Message(msg)
default:
actions.Action(msg)
}
}

39
main.go
View File

@ -1,42 +1,31 @@
package main
import (
"flag"
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
_ "github.com/toby3d/MyPackBot/init"
"github.com/toby3d/MyPackBot/internal/updates"
)
// bot is general structure of the bot
var bot *tg.Bot
var flagWebhook = flag.Bool(
"webhook", false,
"enable work via webhooks (required valid certificates)",
)
// main function is a general function for work of this bot
func main() {
log.Ln("Let'g Get It Started...")
var err error
flag.Parse() // Parse flagWebhook
go dbInit()
defer func() {
err = db.Close()
errCheck(err)
}()
log.Ln("Initializing new bot via checking access_token...")
bot, err = tg.NewBot(cfg.UString("telegram.token"))
errCheck(err)
log.Ln("Let's check updates channel!")
for update := range getUpdatesChannel() {
for update := range updates.Channel(*flagWebhook) {
log.D(update)
switch {
case update.IsInlineQuery():
log.D(update.InlineQuery)
updateInlineQuery(update.InlineQuery)
updates.InlineQuery(update.InlineQuery)
case update.IsMessage():
log.D(update.Message)
updateMessage(update.Message)
updates.Message(update.Message)
case update.IsChannelPost():
log.D(update.ChannelPost)
updateChannelPost(update.ChannelPost)
default:
log.D(update)
updates.ChannelPost(update.ChannelPost)
}
}
}

View File

@ -1,29 +0,0 @@
package main
import (
"strings"
tg "github.com/toby3d/telegram"
)
func messages(msg *tg.Message) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
switch {
case strings.EqualFold(msg.Text, T("button_add_sticker")):
commandAdd(msg, false)
case strings.EqualFold(msg.Text, T("button_add_pack")):
commandAdd(msg, true)
case strings.EqualFold(msg.Text, T("button_del_sticker")):
commandDelete(msg, false)
case strings.EqualFold(msg.Text, T("button_del_pack")):
commandDelete(msg, true)
case strings.EqualFold(msg.Text, T("button_reset")):
commandReset(msg)
case strings.EqualFold(msg.Text, T("button_cancel")):
commandCancel(msg)
case strings.EqualFold(msg.Text, T("meta_key_phrase")):
actions(msg)
}
}

View File

@ -1,71 +0,0 @@
package main
import (
"strings"
tg "github.com/toby3d/telegram"
)
func commandReset(msg *tg.Message) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
_, total, err := dbGetUserStickers(msg.From.ID, 0, "")
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
if total <= 0 {
err = dbChangeUserState(msg.From.ID, stateNone)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("error_already_reset"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
return
}
err = dbChangeUserState(msg.From.ID, stateReset)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("reply_reset", map[string]interface{}{
"KeyPhrase": T("key_phrase"),
"CancelCommand": cmdCancel,
}))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getCancelButton(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}
func actionReset(msg *tg.Message) {
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
err = dbChangeUserState(msg.From.ID, stateNone)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
if !strings.EqualFold(msg.Text, T("key_phrase")) {
reply := tg.NewMessage(msg.Chat.ID, T("error_reset_phrase"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
return
}
err = dbResetUserStickers(msg.From.ID)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("success_reset"))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}

View File

@ -1,37 +0,0 @@
package main
import (
"strings"
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
func commandStart(msg *tg.Message) {
err := dbChangeUserState(msg.From.ID, stateNone)
errCheck(err)
_, err = bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
errCheck(err)
if msg.HasCommandArgument() {
log.Ln("Received a", msg.Command(), "command with", msg.CommandArgument(), "argument")
if strings.ToLower(msg.CommandArgument()) == strings.ToLower(cmdHelp) {
commandHelp(msg)
return
}
}
T, err := switchLocale(msg.From.LanguageCode)
errCheck(err)
reply := tg.NewMessage(msg.Chat.ID, T("reply_start", map[string]interface{}{
"Username": bot.Self.Username,
"ID": bot.Self.ID,
}))
reply.ParseMode = tg.ModeMarkdown
reply.ReplyMarkup = getMenuKeyboard(T)
_, err = bot.SendMessage(reply)
errCheck(err)
}

View File

@ -1,13 +0,0 @@
package main
import (
log "github.com/kirillDanshin/dlog"
"github.com/nicksnyder/go-i18n/i18n"
)
const langFallback = "en"
func switchLocale(langCode string) (i18n.TranslateFunc, error) {
log.Ln("Check", langCode, "localization")
return i18n.Tfunc(langCode, langFallback)
}

0
i18n/en.all.yaml → translations/en.all.yaml Executable file → Normal file
View File

0
i18n/ru.all.yaml → translations/ru.all.yaml Executable file → Normal file
View File

View File

@ -7,4 +7,4 @@ success_del_sticker:
success_del_pack:
other: The sticker pack *{{.SetTitle}}* was successfully removed from yours!
success_reset:
other: The contents of your pack are completely reset!..
other: The contents of your pack are completely reset!

View File

@ -7,4 +7,4 @@ success_del_sticker:
success_del_pack:
other: Набор *{{.SetTitle}}* успешно удалён из твоего набора!
success_reset:
other: Сброс твоего набора успешно произведён!..
other: Сброс твоего набора успешно произведён!

View File

@ -1,33 +0,0 @@
package main
import (
"sync"
"time"
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
var waitForwards = new(sync.WaitGroup)
func updateChannelPost(post *tg.Message) {
if post.Chat.ID != channelID {
log.Ln(post.Chat.ID, "!=", channelID)
return
}
users, err := dbGetUsers()
errCheck(err)
for i := range users {
waitForwards.Add(1)
if _, err = bot.ForwardMessage(
tg.NewForwardMessage(post.Chat.ID, int64(users[i]), post.ID),
); err != nil {
log.Ln(err.Error())
}
waitForwards.Done()
time.Sleep(time.Second / 10) // For avoid spamming
}
}

View File

@ -1,22 +0,0 @@
package main
import (
log "github.com/kirillDanshin/dlog"
tg "github.com/toby3d/telegram"
)
func updateMessage(msg *tg.Message) {
if bot.IsMessageFromMe(msg) || bot.IsForwardFromMe(msg) {
log.Ln("Ignore message update")
return
}
switch {
case bot.IsCommandToMe(msg):
commands(msg)
case msg.IsText():
messages(msg)
default:
actions(msg)
}
}