Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
|
@ -1,23 +0,0 @@
|
|||
### Go ###
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Project-local glide cache
|
||||
# RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
# Production files must not be in public repository
|
||||
MyPackBot
|
||||
stickers.db
|
||||
configs/config.yaml
|
||||
cert.key
|
||||
cert.pem
|
|
@ -1,47 +0,0 @@
|
|||
image: golang:alpine
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- /go/src/github.com
|
||||
- /go/src/gitlab.com
|
||||
- /go/src/golang.org
|
||||
- /go/src/google.golang.org
|
||||
- /go/src/gopkg.in
|
||||
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
|
||||
before_script:
|
||||
- apk add --no-cache git build-base bash
|
||||
- mkdir -p /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds
|
||||
- cp -r $CI_PROJECT_DIR /go/src/gitlab.com/$CI_PROJECT_PATH
|
||||
- ln -s /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds/$CI_PROJECT_NAMESPACE
|
||||
- make dep
|
||||
|
||||
unit_tests:
|
||||
stage: test
|
||||
script:
|
||||
- make test
|
||||
|
||||
.race_detector:
|
||||
stage: test
|
||||
script:
|
||||
- make race
|
||||
|
||||
code_coverage:
|
||||
stage: test
|
||||
script:
|
||||
- make coverage
|
||||
|
||||
lint_code:
|
||||
stage: test
|
||||
script:
|
||||
- go get github.com/go-critic/go-critic/cmd/gocritic
|
||||
- go install github.com/go-critic/go-critic/cmd/gocritic
|
||||
- make lint
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- make
|
201
LICENSE
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
32
Makefile
|
@ -1,32 +0,0 @@
|
|||
PROJECT_NAMESPACE := $(CI_PROJECT_NAMESPACE)
|
||||
PROJECT_NAME := $(CI_PROJECT_NAME)
|
||||
PROJECT_PATH := $(PROJECT_NAMESPACE)/$(PROJECT_NAME)
|
||||
PACKAGE_NAME := "gitlab.com/$(PROJECT_PATH)"
|
||||
PACKAGE_PATH := $(GOPATH)/src/$(PACKAGE_NAME)
|
||||
PACKAGE_LIST := $(shell go list $(PACKAGE_NAME)/... | grep -v /vendor/)
|
||||
GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)
|
||||
|
||||
.PHONY: all lint test rase coverage dep build clean
|
||||
|
||||
all: build
|
||||
|
||||
lint: ## Lint the files
|
||||
@gocritic check-project $(PACKAGE_PATH)
|
||||
|
||||
race: dep ## Run data race detector
|
||||
@go test -race -short ${PACKAGE_LIST}
|
||||
|
||||
coverage: ## Generate global code coverage report
|
||||
@go test -cover -v -coverpkg=$(PACKAGE_NAME) ${PACKAGE_LIST}
|
||||
|
||||
dep: ## Get the dependencies
|
||||
@go get -v -d -t ${PACKAGE_LIST}
|
||||
|
||||
build: dep ## Build the binary file
|
||||
@go build -i -v $(PACKAGE_NAME)
|
||||
|
||||
clean: ## Remove previous build
|
||||
@rm -f $(PROJECT_NAME)
|
||||
|
||||
help: ## Display this help screen
|
||||
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
|
@ -1,7 +0,0 @@
|
|||
telegram:
|
||||
token: 123456789:ABCd1efGhjKLM23O4pqR5stuvwx678yz90
|
||||
webhook:
|
||||
set: https://toby3d.github.io
|
||||
listen: /bot
|
||||
serve: 0.0.0.0:2368
|
||||
channel: -1000000000000
|
|
@ -1,13 +0,0 @@
|
|||
version: "3"
|
||||
services:
|
||||
bot:
|
||||
container_name: mypackbot
|
||||
image: golang:latest
|
||||
expose:
|
||||
- 2368
|
||||
volumes:
|
||||
- ./MyPackBot:/go/MyPackBot
|
||||
- ./translations/:/go/translations/
|
||||
- ./configs/config.yaml:/go/configs/config.yaml
|
||||
- ./stickers.db:/go/stickers.db
|
||||
entrypoint: ["/go/MyPackBot", "-webhook"]
|
|
@ -1,39 +0,0 @@
|
|||
# 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/
|
|
@ -1,43 +0,0 @@
|
|||
# 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.
|
|
@ -1,78 +0,0 @@
|
|||
# [@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)
|
||||
[![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)
|
||||
|
||||
## Wat?
|
||||
This is a Telegram-bot that collects all the stickers sent to it in one (almost) infinite pack. No more, no less.
|
||||
|
||||
**Benefits:**
|
||||
- Does not require creation of a set with a unique URL and/or name;
|
||||
- Indeed (almost) unlimited pack size;
|
||||
- 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's: `@MyPackBot 😀👍`;
|
||||
- Fast as f\*\*\*king Sonic;
|
||||
- Worked with uploadable WebP stickers;
|
||||
- Worked with blocked by rightholders sets (but this is not exact);
|
||||
|
||||
**Disadvantages:**
|
||||
- Requires type `@MyPackBot ` in the input field;
|
||||
- Availability depends on the internet connection and bot uptime;
|
||||
- Supports search/filtering only for first emoji associated with sticker;
|
||||
- Does not support synchronization of the updated original set contents with the saved set contents in the bot.
|
||||
|
||||
## Why?
|
||||
Because Telegram native tools for managing stickers are somewhat limited:
|
||||
- User can have only **up to 200 active stickers sets**;
|
||||
- In one set can be uploaded **up to 120 stickers**;
|
||||
- User can have only **up to 5 favorites stickers**;
|
||||
|
||||
Having done simple mathematical calculations, we can assume that the **maximum user capacity** (when he has the maximum number of sets, each of which contains the maximum number of stickers) **is equal 24,000 stickers**.
|
||||
|
||||
But, as usual, there are problems:
|
||||
- **Most of the sets are incomplete** and contain less than 120 stickers (sometimes - only 1-3 stickers on whole set);
|
||||
- **Some sets contains junk, duplicated and promotional stickers**;
|
||||
- **Sometimes user want use own stickers** by uploading WebP files, but without creating new sticker set;
|
||||
- Anyway, **user just want have as many stickers as he want**;
|
||||
|
||||
To solve these problems, this bot was designed.
|
||||
|
||||
## How?
|
||||
### tl;dr
|
||||
- Telegram API [supports stickers as results](https://core.telegram.org/bots/api#inlinequeryresultcachedsticker) in inline query;
|
||||
- Telegram API allows to use someone else's FileID for results;
|
||||
- It is not necessary to [create a new set](https://core.telegram.org/bots/api#createnewstickerset) using Telegram, since it only "references" existing files;
|
||||
- Bot saves only [user](https://core.telegram.org/bots/api#user) info, [sticker and name of his set](https://core.telegram.org/bots/api#sticker) in the database if user upload custom sticker or send/forward already existing;
|
||||
- Database architecture allows to filter keys by user ID and sort them by set name and emoji value;
|
||||
- When requesting inline query, bot simply create results from filtered database keys;
|
||||
- ???????
|
||||
- PROFIT!!1
|
||||
|
||||
## Step-by-step
|
||||
I'm too lazy to write, so just check the source code for the comments. 👀
|
||||
|
||||
### Dependencies
|
||||
Bot uses the following dependencies:
|
||||
- Written on [Go](https://github.com/golang/go) language, because I <3 Go;
|
||||
- I ventured to migrate to my own [telegram](https://github.com/toby3d/telegram) package to win in convenience and productivity;
|
||||
- I use [dlog](https://github.com/kirillDanshin/dlog) for debugging without spamming on production server by use only one build flag;
|
||||
- Data of users and stickers save thanks to [BuntDB](https://github.com/tidwall/buntdb);
|
||||
|
||||
## 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/translations) on your language.
|
||||
|
||||
Ah, and star this repo, of course.
|
||||
|
||||
### Patreon
|
||||
**I work on my own projects in my free time.** Please think about the [financial support for my independence](https://patreon.com/toby3d) so that I can devote more time to this bot and other projects. In exchange for an award!
|
||||
|
||||
### Social
|
||||
Subscribe, follow my resources and feel free to maintain contact with me: https://toby3d.github.io
|
|
@ -1,9 +0,0 @@
|
|||
# [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:**
|
||||
- Aurielb
|
||||
- Daniil Tlenov
|
||||
- @kirillDanshin
|
||||
- MoD21k
|
||||
- @YamiOdymel
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh
|
||||
# gitmoji as a commit hook
|
||||
exec < /dev/tty
|
||||
gitmoji --hook $1
|
101
i18n/en.all.yaml
|
@ -1,101 +0,0 @@
|
|||
button_add_pack:
|
||||
other: "\U0001F4E6 Add pack"
|
||||
button_add_sticker:
|
||||
other: ➕ Add sticker
|
||||
button_cancel:
|
||||
other: ❌ Cancel
|
||||
button_del_pack:
|
||||
other: "\U0001F5D1 Delete pack"
|
||||
button_del_sticker:
|
||||
other: "\U0001F5D1 Delete sticker"
|
||||
button_inline_empty:
|
||||
other: Your pack is empty
|
||||
button_inline_nothing:
|
||||
other: Not found stickers for {{.Query}}
|
||||
button_inline_search:
|
||||
one: You have {{.Count}} sticker
|
||||
other: You have {{.Count}} stickers
|
||||
button_inline_select:
|
||||
other: Select sticker
|
||||
button_reset:
|
||||
other: "\U0001F525 Reset pack"
|
||||
button_share:
|
||||
other: Use your stickers pack!
|
||||
cancel_add_pack:
|
||||
other: You cancelled the process of adding a new packs to yours.
|
||||
cancel_add_sticker:
|
||||
other: You cancelled the process of adding a new stickers to your pack.
|
||||
cancel_del_pack:
|
||||
other: You cancelled the process of removing a sticker packs from yours.
|
||||
cancel_del_sticker:
|
||||
other: You cancelled the process of removing a stickers from your pack.
|
||||
cancel_error:
|
||||
other: Nothing to cancel.
|
||||
cancel_reset:
|
||||
other: You cancelled the process of reseting your stickers pack.
|
||||
error_already_add_pack:
|
||||
other: All stickers from *{{.SetTitle}}* pack is already in yours.
|
||||
error_already_add_sticker:
|
||||
other: This sticker is already in your pack.
|
||||
error_already_del_pack:
|
||||
other: Maybe this pack is already removed from yours.
|
||||
error_already_del_sticker:
|
||||
other: Maybe this sticker is already removed from your pack.
|
||||
error_already_reset:
|
||||
other: There is nothing to reset, pack is already empty.
|
||||
error_empty_add_pack:
|
||||
other: You try to add a sticker that exists outside the packs. Use /{{.AddStickerCommand}}
|
||||
instead.
|
||||
error_empty_del:
|
||||
other: There is nothing to remove, pack is empty.
|
||||
error_reset_phrase:
|
||||
other: Invalid phrase of resetting. This action has been canceled.
|
||||
error_unknown:
|
||||
other: |-
|
||||
I do not know what to do with this sticker.
|
||||
Please run /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} or /{{.DeletePackCommand}} command first.
|
||||
key_phrase:
|
||||
other: Yes, I am totally sure.
|
||||
reply_add_pack:
|
||||
other: Send an existing stickers from any other packs to add the entire packs to
|
||||
yourself.
|
||||
reply_add_sticker:
|
||||
other: Send an existing stickers from any other packs to add it to yourself.
|
||||
reply_del_pack:
|
||||
other: Send an existing stickers from your pack to delete its entire set.
|
||||
reply_del_sticker:
|
||||
other: Send an existing stickers from your pack for removing it.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - add a single sticker to your pack
|
||||
/{{.AddPackCommand}} - add a full other pack to your pack
|
||||
/{{.DeleteStickerCommand}} - remove a single sticker from your pack
|
||||
/{{.DeletePackCommand}} - remove a sticker set from your pack
|
||||
/{{.ResetCommand}} - remove all stickers from your pack
|
||||
/{{.CancelCommand}} - cancel the current operation
|
||||
|
||||
To view and send stickers from your pack, just type `@{{.Username}}` (and space) in any chat.
|
||||
reply_reset:
|
||||
other: |
|
||||
This operation will remove *all* stickers from your pack and *this can't be undone*.
|
||||
|
||||
Send `{{.KeyPhrase}}` to confirm what you really want to reset my brain (oh god why).
|
||||
Or use /{{.CancelCommand}} for abort current operation.
|
||||
reply_start:
|
||||
other: |
|
||||
Hello, I'm the [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
I can create your personal pack with stickers from others packs.
|
||||
Without limits and installing. In any chat. For free.
|
||||
reply_switch_button:
|
||||
other: This button will help you quickly call your pack to select the sticker you
|
||||
want.
|
||||
success_add_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully added to yours!
|
||||
success_add_sticker:
|
||||
other: The sticker was successfully added to your pack!
|
||||
success_del_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully removed from yours!
|
||||
success_del_sticker:
|
||||
other: The sticker was successfully removed from your pack!
|
||||
success_reset:
|
||||
other: The contents of your pack are completely reset!..
|
|
@ -1 +0,0 @@
|
|||
{}
|
101
i18n/ru.all.yaml
|
@ -1,101 +0,0 @@
|
|||
button_add_pack:
|
||||
other: "\U0001F4E6 Добавить набор"
|
||||
button_add_sticker:
|
||||
other: ➕ Добавить стикер
|
||||
button_cancel:
|
||||
other: ❌ Отменить
|
||||
button_del_pack:
|
||||
other: "\U0001F5D1 Удалить набор"
|
||||
button_del_sticker:
|
||||
other: "\U0001F5D1 Удалить стикер"
|
||||
button_inline_empty:
|
||||
other: Твой набор пуст
|
||||
button_inline_nothing:
|
||||
other: Не найдены стикеры для {{.Query}}
|
||||
button_inline_search:
|
||||
few: У тебя {{.Count}} стикера
|
||||
many: У тебя {{.Count}} стикеров
|
||||
one: У тебя {{.Count}} стикер
|
||||
other: У тебя {{.Count}} стикеров
|
||||
button_inline_select:
|
||||
other: Выбрать стикер
|
||||
button_reset:
|
||||
other: "\U0001F525 Сбросить набор"
|
||||
button_share:
|
||||
other: Воспользоваться твоим набором!
|
||||
cancel_add_pack:
|
||||
other: Ты отменил процесс добавления новых наборов в твой.
|
||||
cancel_add_sticker:
|
||||
other: Ты отменил процесс добавления новых стикеров в твой набор.
|
||||
cancel_del_pack:
|
||||
other: Ты отменил процесс удаления наборов из твоего набора.
|
||||
cancel_del_sticker:
|
||||
other: Ты отменил процесс удаления стикера из твоего набора.
|
||||
cancel_error:
|
||||
other: Нечего отменять.
|
||||
cancel_reset:
|
||||
other: Ты отменил процесс сброса твоего набора.
|
||||
error_already_add_pack:
|
||||
other: Все стикеры *{{.SetTitle}}* уже в твоём наборе.
|
||||
error_already_add_sticker:
|
||||
other: Этот стикер уже в твоём наборе.
|
||||
error_already_del_pack:
|
||||
other: Вероятно этот набор уже удалён из твоего.
|
||||
error_already_del_sticker:
|
||||
other: Вероятно этот стикер уже удалён из твоего набора.
|
||||
error_already_reset:
|
||||
other: Нечего сбрасывать, набор уже пуст.
|
||||
error_empty_add_pack:
|
||||
other: Кажется ты пытаешься добавить собственный стикер. Используй для этого /{{.AddStickerCommand}}.
|
||||
error_empty_del:
|
||||
other: Нечего удалять, набор уже пуст.
|
||||
error_reset_phrase:
|
||||
other: Неправильная фраза для сброса. Действие было отменено.
|
||||
error_unknown:
|
||||
other: |-
|
||||
Я понятия не имею что делать с этим стикером.
|
||||
Пожалуйста, сначала примени /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} или /{{.DeletePackCommand}}.
|
||||
key_phrase:
|
||||
other: Да, я абсолютно уверен.
|
||||
reply_add_pack:
|
||||
other: Пришли стикеры из любых других наборов чтобы целиком добавить их наборы в
|
||||
свой.
|
||||
reply_add_sticker:
|
||||
other: Пришли стикеры из любых других наборов чтобы по-одному добавить их в свой.
|
||||
reply_del_pack:
|
||||
other: Пришли стикер из своего набора чтобы удалить весь его набор.
|
||||
reply_del_sticker:
|
||||
other: Пришли стикер из своего набора чтобы удалить его.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - по-одному добавляет стикеры в твой набор
|
||||
/{{.AddPackCommand}} - добавляет сразу весь набор в твой
|
||||
/{{.DeleteStickerCommand}} - по-одному удаляет стикер из твоего набора
|
||||
/{{.DeletePackCommand}} - удаляет набор стикеров из твоего набора
|
||||
/{{.ResetCommand}} - удаляет все стикеры из твоего набора
|
||||
/{{.CancelCommand}} - отменяет текущую операцию
|
||||
|
||||
Для просмотра и отправки стикеров из твоего набора просто набери `@{{.Username}}` (и пробел) в любом чате.
|
||||
reply_reset:
|
||||
other: |
|
||||
Эта операция удалит *все* стикеры из твоего набора и *это не может быть отменено*.
|
||||
|
||||
Напиши `{{.KeyPhrase}}` чтобы подтвердить своё намерение обнулить мои мозги (о боже зачем).
|
||||
Или используй /{{.CancelCommand}} чтобы отменить текущую операцию.
|
||||
reply_start:
|
||||
other: |
|
||||
Привет, я [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
Я могу создать твой персональный набор стикеров из других наборов.
|
||||
Без ограничений и установки. В любых чатах. Бесплатно.
|
||||
reply_switch_button:
|
||||
other: Эта кнопка поможет тебе быстро вызвать твой набор для выбора нужного стикера.
|
||||
success_add_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно добавлен в твой!
|
||||
success_add_sticker:
|
||||
other: Стикер успешно добавлен в твой набор!
|
||||
success_del_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно удалён из твоего набора!
|
||||
success_del_sticker:
|
||||
other: Стикер успешно удалён из твоего набора!
|
||||
success_reset:
|
||||
other: Сброс твоего набора успешно произведён!..
|
|
@ -1 +0,0 @@
|
|||
{}
|
|
@ -1,23 +0,0 @@
|
|||
button_inline_empty:
|
||||
other: Your pack is empty
|
||||
button_inline_nothing:
|
||||
other: Not found stickers for {{.Query}}
|
||||
button_inline_search:
|
||||
one: You have {{.Count}} sticker
|
||||
other: You have {{.Count}} stickers
|
||||
button_inline_select:
|
||||
other: Select sticker
|
||||
button_share:
|
||||
other: Use your stickers pack!
|
||||
button_add_sticker:
|
||||
other: ➕ Add sticker
|
||||
button_add_pack:
|
||||
other: 📦 Add set
|
||||
button_del_sticker:
|
||||
other: 🗑 Delete sticker
|
||||
button_del_pack:
|
||||
other: 🗑 Delete set
|
||||
button_reset:
|
||||
other: 🔥 Reset pack
|
||||
button_cancel:
|
||||
other: ❌ Cancel
|
|
@ -1,12 +0,0 @@
|
|||
cancel_add_sticker:
|
||||
other: You cancelled the process of adding a new stickers to your pack.
|
||||
cancel_add_pack:
|
||||
other: You cancelled the process of adding a new sets to your pack.
|
||||
cancel_del_sticker:
|
||||
other: You cancelled the process of removing a stickers from your pack.
|
||||
cancel_del_pack:
|
||||
other: You cancelled the process of removing a sets from your pack.
|
||||
cancel_reset:
|
||||
other: You cancelled the process of reseting your stickers pack.
|
||||
cancel_error:
|
||||
other: Nothing to cancel.
|
|
@ -1,20 +0,0 @@
|
|||
error_already_add_sticker:
|
||||
other: This sticker is already in your pack.
|
||||
error_already_add_pack:
|
||||
other: All stickers from *{{.SetTitle}}* set is already in yours.
|
||||
error_already_del_sticker:
|
||||
other: Maybe this sticker is already removed from your pack.
|
||||
error_already_del_pack:
|
||||
other: Maybe this set is already removed from your pack.
|
||||
error_already_reset:
|
||||
other: There is nothing to reset, pack is already empty.
|
||||
error_reset_phrase:
|
||||
other: Invalid phrase of resetting. This action has been canceled.
|
||||
error_empty_del:
|
||||
other: There is nothing to remove, your pack is empty.
|
||||
error_empty_add_pack:
|
||||
other: You try to add a sticker that exists outside the packs. Use /{{.AddStickerCommand}} instead.
|
||||
error_unknown:
|
||||
other: |
|
||||
I do not know what to do with this sticker.
|
||||
Please run /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} or /{{.DeletePackCommand}} command first.
|
|
@ -1,31 +0,0 @@
|
|||
reply_start:
|
||||
other: |
|
||||
Hello, I'm the [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
I can create your personal stickers pack with stickers from others sets.
|
||||
Without limits and installing. In any chat. For free.
|
||||
reply_add_sticker:
|
||||
other: Send an existing stickers from any other sets to add it to your pack.
|
||||
reply_add_pack:
|
||||
other: Send an existing stickers from any other sets to add the entire sets to your pack.
|
||||
reply_del_sticker:
|
||||
other: Send an existing stickers from your pack for removing it.
|
||||
reply_del_pack:
|
||||
other: Send an existing stickers from your pack to delete its entire set.
|
||||
reply_reset:
|
||||
other: |
|
||||
This operation will remove *all* stickers from your pack and *this can't be undone*.
|
||||
|
||||
Send `{{.KeyPhrase}}` to confirm what you really want to reset my brain (oh god why).
|
||||
Or use /{{.CancelCommand}} for abort current operation.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - add a single sticker to your pack
|
||||
/{{.AddPackCommand}} - add a full stickers set to your pack
|
||||
/{{.DeleteStickerCommand}} - remove a single sticker from your pack
|
||||
/{{.DeletePackCommand}} - remove a sticker set from your pack
|
||||
/{{.ResetCommand}} - remove all stickers from your pack
|
||||
/{{.CancelCommand}} - cancel the current operation
|
||||
|
||||
To view and send stickers from your pack, just type `@{{.Username}}` (and space) in any chat.
|
||||
reply_switch_button:
|
||||
other: This button will help you quickly call your pack to select the sticker you want.
|
|
@ -1,10 +0,0 @@
|
|||
success_add_sticker:
|
||||
other: The sticker was successfully added to your pack!
|
||||
success_add_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully added to yours!
|
||||
success_del_sticker:
|
||||
other: The sticker was successfully removed from your pack!
|
||||
success_del_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully removed from yours!
|
||||
success_reset:
|
||||
other: The contents of your pack are completely reset!
|
|
@ -1,2 +0,0 @@
|
|||
key_phrase:
|
||||
other: Yes, I am totally sure.
|
|
@ -1,25 +0,0 @@
|
|||
button_inline_empty:
|
||||
other: Твой набор пуст
|
||||
button_inline_nothing:
|
||||
other: Не найдены стикеры для {{.Query}}
|
||||
button_inline_search:
|
||||
few: У тебя {{.Count}} стикера
|
||||
one: У тебя {{.Count}} стикер
|
||||
many: У тебя {{.Count}} стикеров
|
||||
other: У тебя {{.Count}} стикеров
|
||||
button_inline_select:
|
||||
other: Выбрать стикер
|
||||
button_share:
|
||||
other: Воспользоваться твоим набором!
|
||||
button_add_sticker:
|
||||
other: ➕ Добавить стикер
|
||||
button_add_pack:
|
||||
other: 📦 Добавить набор
|
||||
button_del_sticker:
|
||||
other: 🗑 Удалить стикер
|
||||
button_del_pack:
|
||||
other: 🗑 Удалить набор
|
||||
button_reset:
|
||||
other: 🔥 Сбросить набор
|
||||
button_cancel:
|
||||
other: ❌ Отменить
|
|
@ -1,12 +0,0 @@
|
|||
cancel_add_sticker:
|
||||
other: Ты отменил процесс добавления новых стикеров в твой набор.
|
||||
cancel_add_pack:
|
||||
other: Ты отменил процесс добавления новых наборов в твой.
|
||||
cancel_del_sticker:
|
||||
other: Ты отменил процесс удаления стикера из твоего набора.
|
||||
cancel_del_pack:
|
||||
other: Ты отменил процесс удаления наборов из твоего набора.
|
||||
cancel_reset:
|
||||
other: Ты отменил процесс сброса твоего набора.
|
||||
cancel_error:
|
||||
other: Нечего отменять.
|
|
@ -1,20 +0,0 @@
|
|||
error_already_add_sticker:
|
||||
other: Этот стикер уже в твоём наборе.
|
||||
error_already_add_pack:
|
||||
other: Все стикеры *{{.SetTitle}}* уже в твоём наборе.
|
||||
error_already_del_sticker:
|
||||
other: Вероятно этот стикер уже удалён из твоего набора.
|
||||
error_already_del_pack:
|
||||
other: Вероятно этот набор уже удалён из твоего.
|
||||
error_already_reset:
|
||||
other: Нечего сбрасывать, набор уже пуст.
|
||||
error_reset_phrase:
|
||||
other: Неправильная фраза для сброса. Действие было отменено.
|
||||
error_empty_del:
|
||||
other: Нечего удалять, набор уже пуст.
|
||||
error_empty_add_pack:
|
||||
other: Кажется ты пытаешься добавить собственный стикер. Используй для этого /{{.AddStickerCommand}}.
|
||||
error_unknown:
|
||||
other: |
|
||||
Я понятия не имею что делать с этим стикером.
|
||||
Пожалуйста, сначала примени /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} или /{{.DeletePackCommand}}.
|
|
@ -1,31 +0,0 @@
|
|||
reply_start:
|
||||
other: |
|
||||
Привет, я [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
Я могу создать твой персональный набор стикеров из других наборов.
|
||||
Без ограничений и установки. В любых чатах. Бесплатно.
|
||||
reply_add_sticker:
|
||||
other: Пришли стикеры из любых других наборов чтобы по-одному добавить их в свой.
|
||||
reply_add_pack:
|
||||
other: Пришли стикеры из любых других наборов чтобы целиком добавить их наборы в свой.
|
||||
reply_del_sticker:
|
||||
other: Пришли стикер из своего набора чтобы удалить его.
|
||||
reply_del_pack:
|
||||
other: Пришли стикер из своего набора чтобы удалить весь его набор.
|
||||
reply_reset:
|
||||
other: |
|
||||
Эта операция удалит *все* стикеры из твоего набора и *это не может быть отменено*.
|
||||
|
||||
Напиши `{{.KeyPhrase}}` чтобы подтвердить своё намерение обнулить мои мозги (о боже зачем).
|
||||
Или используй /{{.CancelCommand}} чтобы отменить текущую операцию.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - по-одному добавляет стикеры в твой набор
|
||||
/{{.AddPackCommand}} - добавляет сразу весь набор в твой
|
||||
/{{.DeleteStickerCommand}} - по-одному удаляет стикер из твоего набора
|
||||
/{{.DeletePackCommand}} - удаляет набор стикеров из твоего набора
|
||||
/{{.ResetCommand}} - удаляет все стикеры из твоего набора
|
||||
/{{.CancelCommand}} - отменяет текущую операцию
|
||||
|
||||
Для просмотра и отправки стикеров из твоего набора просто набери `@{{.Username}}` (и пробел) в любом чате.
|
||||
reply_switch_button:
|
||||
other: Эта кнопка поможет тебе быстро вызвать твой набор для выбора нужного стикера.
|
|
@ -1,10 +0,0 @@
|
|||
success_add_sticker:
|
||||
other: Стикер успешно добавлен в твой набор!
|
||||
success_add_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно добавлен в твой!
|
||||
success_del_sticker:
|
||||
other: Стикер успешно удалён из твоего набора!
|
||||
success_del_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно удалён из твоего набора!
|
||||
success_reset:
|
||||
other: Сброс твоего набора успешно произведён!
|
|
@ -1,2 +0,0 @@
|
|||
key_phrase:
|
||||
other: Да, я абсолютно уверен.
|
|
@ -0,0 +1,135 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>My Stickers Pack</title>
|
||||
<meta name="description" content="Your own unlimited pack of Telegram-stickers">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="og:type" content="website">
|
||||
<meta name="og:site_name" content="toby3d">
|
||||
<meta name="og:title" content="My Stickers Pack">
|
||||
<meta name="og:description" content="Your own unlimited pack of Telegram-stickers">
|
||||
<meta name="og:url" content="https://toby3d.github.io/MyPackBot/">
|
||||
<meta name="og:locale" content="en_US">
|
||||
<meta name="og:image" content="static/social/og-image.jpg">
|
||||
<meta name="og:image:width" content="1024">
|
||||
<meta name="og:image:height" content="512">
|
||||
|
||||
<meta name="twitter:title" content="My Stickers Pack">
|
||||
<meta name="twitter:description" content="Your own unlimited pack of Telegram-stickers">
|
||||
<meta name="twitter:image:src" content="static/social/og-image.jpg">
|
||||
<meta name="twitter:url" content="https://toby3d.github.io/MyPackBot/">
|
||||
<meta name="twitter:domain" content="toby3d.github.io">
|
||||
<meta name="twitter:site" content="@toby3d">
|
||||
<meta name="twitter:creator" content="@toby3d">
|
||||
|
||||
<meta name="fb:profile_id" content="100001181798453">
|
||||
|
||||
<meta name="apple-mobile-web-app-title" content="My Stickers Pack">
|
||||
<meta name="application-name" content="My Stickers Pack">
|
||||
<meta name="msapplication-config" content="static/favicon/browserconfig.xml">
|
||||
<meta name="theme-color" content="#56b0ef">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="static/favicon/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="static/favicon/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="static/favicon/favicon-16x16.png">
|
||||
<link rel="manifest" href="static/favicon/manifest.json">
|
||||
<link rel="mask-icon" href="static/favicon/safari-pinned-tab.svg" color="#56b0ef">
|
||||
<link rel="shortcut icon" href="static/favicon/favicon.ico">
|
||||
|
||||
<link rel="canonical" href="https://toby3d.github.io/MyPackBot/">
|
||||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/tocas-ui/2.3.3/tocas.css">
|
||||
<link rel="stylesheet" href="static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="ts big heading slate valign-wrapper">
|
||||
<div class="ts valign center aligned basic fitted segment">
|
||||
<img class="ts medium centered image" src="static/img/logo.png" alt="My Stickers Pack">
|
||||
<br>
|
||||
<span class="bold header">My Stickers Pack</span>
|
||||
<span class="description">Your own unlimited pack of Telegram-stickers</span>
|
||||
<br>
|
||||
<div class="ts relaxed separated stackable buttons">
|
||||
<a class="ts secondary opinion button" href="//t.me/MyPackBot">Open @MyPackBot</a>
|
||||
<a href="//patreon.com/bePatron?c=243288">
|
||||
<img class="ts link image button" src="static/img/patreon.png">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="ts container">
|
||||
<div class="ts three stackable cards">
|
||||
<div class="ts card">
|
||||
<div class="image">
|
||||
<img src="static/img/addSticker.jpg" alt="adding sticker">
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="header">Adding stickers</div>
|
||||
<div class="description">Add stickers from any packs (or upload your own WebP's) one-by-one and they will be in your pack immediately!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ts card">
|
||||
<div class="image">
|
||||
<img src="static/img/addPack.jpg" alt="adding pack">
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="header">Adding packs</div>
|
||||
<div class="description">Add whole packs by sending just one sticker of them!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ts card">
|
||||
<div class="image">
|
||||
<img src="static/img/del.jpg" alt="deleting sticker">
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="header">Deleting stickers</div>
|
||||
<div class="description">Remove unnecessary stickers from your pack in a couple of clicks!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ts card">
|
||||
<div class="image">
|
||||
<img src="static/img/reset.jpg" alt="resetting your pack">
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="header">Resetting your pack</div>
|
||||
<div class="description">If you are too carried away, then start from scratch!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ts card">
|
||||
<div class="image">
|
||||
<img src="static/img/inlineQuery.jpg" alt="inline query">
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="header">Inline search</div>
|
||||
<div class="description">Get access to all of your stickers in any chats!</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ts card">
|
||||
<div class="image">
|
||||
<img src="static/img/inlineQueryEmoji.jpg" alt="inline query with emoji">
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="header">Search by emoji</div>
|
||||
<div class="description">Look for the necessary stickers via Emojis!</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="ts attached borderless inverted second menu">
|
||||
<div class="ts narrow container">
|
||||
<div class="fitted item">
|
||||
<i class="copyright icon"></i> <a class="ts inverted link button" href="/">M. M. Lebedev</a>, 2017
|
||||
</div>
|
||||
<div class="right menu">
|
||||
<a class="item" href="//tocas-ui.com" data-tooltip-position="left" data-tooltip="Made with ♥ via">Tocas UI</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Yandex.Metrika counter --> <script type="text/javascript" > (function (d, w, c) { (w[c] = w[c] || []).push(function() { try { w.yaCounter21389077 = new Ya.Metrika({ id:21389077, clickmap:true, trackLinks:true, accurateTrackBounce:true, webvisor:true }); } catch(e) { } }); var n = d.getElementsByTagName("script")[0], s = d.createElement("script"), f = function () { n.parentNode.insertBefore(s, n); }; s.type = "text/javascript"; s.async = true; s.src = "//mc.yandex.ru/metrika/watch.js"; if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); } })(document, window, "yandex_metrika_callbacks"); </script> <noscript><div><img src="//mc.yandex.ru/watch/21389077" style="position:absolute; left:-9999px;" alt="" /></div></noscript> <!-- /Yandex.Metrika counter -->
|
||||
<!-- Google Analytics --><script async src="//www.googletagmanager.com/gtag/js?id=UA-79389304-4"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments)}; gtag('js', new Date()); gtag('config', 'UA-79389304-4');</script><!-- /Google Analytics -->
|
||||
</body>
|
||||
</html>
|
|
@ -1,35 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.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.DB.GetUserState(msg.From)
|
||||
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.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
Error(msg)
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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.StyleMarkdown
|
||||
|
||||
if !pack {
|
||||
var exist bool
|
||||
sticker := msg.Sticker
|
||||
exist, err = db.DB.AddSticker(msg.From, sticker)
|
||||
errors.Check(err)
|
||||
|
||||
if exist {
|
||||
reply.Text = t("error_already_add_sticker")
|
||||
}
|
||||
|
||||
reply.ReplyMarkup = utils.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 i := range set.Stickers {
|
||||
var exist bool
|
||||
exist, err = db.DB.AddSticker(msg.From, &set.Stickers[i])
|
||||
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 = utils.CancelButton(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.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.DB.DeletePack(msg.From, msg.Sticker)
|
||||
if notExist {
|
||||
reply.Text = t("error_already_del_pack")
|
||||
}
|
||||
} else {
|
||||
notExist, err = db.DB.DeleteSticker(msg.From, msg.Sticker)
|
||||
if notExist {
|
||||
reply.Text = t("error_already_del_sticker")
|
||||
}
|
||||
}
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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.DB.ChangeUserState(msg.From, 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.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = db.DB.ResetUser(msg.From)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("success_reset"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package bot
|
||||
|
||||
import tg "gitlab.com/toby3d/telegram"
|
||||
|
||||
// Bot is a main object of Telegram bot
|
||||
var Bot *tg.Bot
|
||||
|
||||
// New just create new bot by configuration credentials
|
||||
func New(accessToken string) (bot *tg.Bot, err error) {
|
||||
return tg.New(accessToken)
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateAddSticker)
|
||||
errors.Check(err)
|
||||
|
||||
if pack {
|
||||
reply.Text = t("reply_add_pack")
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateAddPack)
|
||||
errors.Check(err)
|
||||
}
|
||||
|
||||
log.Ln("Sending add reply...")
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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.DB.GetUserState(msg.From)
|
||||
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.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, text)
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Command check's got user command
|
||||
func Command(msg *tg.Message) {
|
||||
log.Ln("command:", msg.Command())
|
||||
switch {
|
||||
case msg.IsCommandEqual(tg.CommandStart):
|
||||
Start(msg)
|
||||
case msg.IsCommandEqual(tg.CommandHelp):
|
||||
Help(msg)
|
||||
case msg.IsCommandEqual(models.CommandAddSticker):
|
||||
Add(msg, false)
|
||||
case msg.IsCommandEqual(models.CommandAddPack):
|
||||
Add(msg, true)
|
||||
case msg.IsCommandEqual(models.CommandDeleteSticker):
|
||||
Delete(msg, false)
|
||||
case msg.IsCommandEqual(models.CommandDeletePack):
|
||||
Delete(msg, true)
|
||||
case msg.IsCommandEqual(models.CommandReset):
|
||||
Reset(msg)
|
||||
case msg.IsCommandEqual(models.CommandCancel):
|
||||
Cancel(msg)
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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)
|
||||
|
||||
stickers, err := db.DB.GetUserStickers(msg.From, &tg.InlineQuery{})
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
if len(stickers) <= 0 {
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("error_empty_del"))
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("reply_del_sticker"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateDeleteSticker)
|
||||
errors.Check(err)
|
||||
|
||||
if pack {
|
||||
err = db.DB.ChangeUserState(msg.From, 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 = utils.SwitchButton(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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.DB.ChangeUserState(msg.From, 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.Username,
|
||||
}),
|
||||
)
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.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)
|
||||
|
||||
stickers, err := db.DB.GetUserStickers(msg.From, &tg.InlineQuery{})
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
if len(stickers) <= 0 {
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("error_already_reset"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, 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.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Start just send introduction about bot to user
|
||||
func Start(msg *tg.Message) {
|
||||
err := db.DB.ChangeUserState(msg.From, 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(), tg.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.Username,
|
||||
"ID": bot.Bot.ID,
|
||||
}),
|
||||
)
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package config
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
|
||||
var Config *viper.Viper
|
||||
|
||||
// Open just open configuration file for parsing some data in other functions
|
||||
func Open(path string) (*viper.Viper, error) {
|
||||
cfg := viper.New()
|
||||
|
||||
cfg.AddConfigPath(path)
|
||||
cfg.SetConfigName("config")
|
||||
cfg.SetConfigType("yaml")
|
||||
|
||||
err := cfg.ReadInConfig()
|
||||
return cfg, err
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// AddSticker add sticker FileID, Emoji and SetName meta for UserID
|
||||
func (db *DataBase) AddSticker(user *tg.User, sticker *tg.Sticker) (bool, error) {
|
||||
log.Ln("Trying to add", sticker.FileID, "sticker from", user.ID, "user")
|
||||
if sticker.SetName == "" {
|
||||
sticker.SetName = models.SetUploaded
|
||||
}
|
||||
|
||||
var exists bool
|
||||
err := db.Update(func(tx *buntdb.Tx) error {
|
||||
var err error
|
||||
_, exists, err = tx.Set(
|
||||
fmt.Sprint("user:", user.ID, ":set:", sticker.SetName, ":sticker:", sticker.FileID), // key
|
||||
sticker.Emoji, // value
|
||||
nil, // options
|
||||
)
|
||||
if err == buntdb.ErrIndexExists {
|
||||
exists = true
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return exists, err
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// ChangeUserState change current user state on input state.
|
||||
func (db *DataBase) ChangeUserState(user *tg.User, state string) error {
|
||||
log.Ln("Trying to change", user.ID, "state to", state)
|
||||
return db.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, err := tx.Set(fmt.Sprint("user:", user.ID, ":state"), state, nil)
|
||||
return err
|
||||
})
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// DeletePack remove all keys for UserID which contains input SetName
|
||||
func (db *DataBase) DeletePack(user *tg.User, sticker *tg.Sticker) (bool, error) {
|
||||
log.Ln("Trying to remove all", sticker.SetName, "sticker from", user.ID, "user")
|
||||
if sticker.SetName == "" {
|
||||
sticker.SetName = models.SetUploaded
|
||||
}
|
||||
|
||||
var ids []string
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
return tx.AscendKeys(
|
||||
fmt.Sprint("user:", user.ID, ":set:", sticker.SetName, ":*"),
|
||||
func(key, val string) bool {
|
||||
keys := strings.Split(key, ":")
|
||||
ids = append(ids, keys[5])
|
||||
return true
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
if len(ids) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
var notExist bool
|
||||
notExist, err = db.DeleteSticker(user, &tg.Sticker{FileID: id})
|
||||
if err != nil {
|
||||
return notExist, err
|
||||
}
|
||||
}
|
||||
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(user.ID, "not found")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// DeleteSticker just remove specified sticker key from database.
|
||||
func (db *DataBase) DeleteSticker(user *tg.User, sticker *tg.Sticker) (bool, error) {
|
||||
log.Ln("Trying to remove", sticker.FileID, "sticker from", user.ID, "user")
|
||||
if sticker.SetName == "" {
|
||||
sticker.SetName = models.SetUploaded
|
||||
}
|
||||
|
||||
err := db.Update(func(tx *buntdb.Tx) error {
|
||||
_, err := tx.Delete(
|
||||
fmt.Sprint("user:", user.ID, ":set:", sticker.SetName, ":sticker:", sticker.FileID),
|
||||
)
|
||||
return err
|
||||
})
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(user.ID, "not found, create new one")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// UserState return current state for UserID
|
||||
func (db *DataBase) GetUserState(user *tg.User) (string, error) {
|
||||
log.Ln("Trying to get", user.ID, "state")
|
||||
var state string
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
var err error
|
||||
state, err = tx.Get(fmt.Sprint("user:", user.ID, ":state"))
|
||||
return err
|
||||
})
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(user.ID, "not found, create new one")
|
||||
if err = db.ChangeUserState(user, models.StateNone); err != nil {
|
||||
return state, err
|
||||
}
|
||||
}
|
||||
|
||||
return state, err
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// GetUserStickers return array of saved stickers for input UserID and his total count
|
||||
func (db *DataBase) GetUserStickers(user *tg.User, query *tg.InlineQuery) ([]string, error) {
|
||||
log.Ln("Trying to get", user.ID, "stickers")
|
||||
var i int
|
||||
var stickers []string
|
||||
offset, _ := strconv.Atoi(query.Offset)
|
||||
offset *= 50
|
||||
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
return tx.AscendKeys(
|
||||
fmt.Sprint("user:", user.ID, ":set:*"), // index
|
||||
func(key, val string) bool { // iterator
|
||||
subKeys := strings.Split(key, ":")
|
||||
if subKeys[1] != strconv.Itoa(user.ID) {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(stickers) == 50 {
|
||||
return false
|
||||
}
|
||||
|
||||
i++
|
||||
if i < offset {
|
||||
return true
|
||||
}
|
||||
|
||||
if query.Query != "" && !strings.Contains(query.Query, val) {
|
||||
return true
|
||||
}
|
||||
|
||||
stickers = append(stickers, subKeys[5])
|
||||
return true
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln("Not found stickers")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return stickers, err
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
// log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
)
|
||||
|
||||
// GetUsers return array of all available UserID in database
|
||||
func (db *DataBase) GetUsers() ([]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
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
)
|
||||
|
||||
type DataBase struct{ *buntdb.DB }
|
||||
|
||||
// DB is a main object of current database connection
|
||||
var DB *DataBase
|
||||
|
||||
// Open just open connection to database for work
|
||||
func Open(path string) (*DataBase, error) {
|
||||
log.Ln("Open database file...")
|
||||
db, err := buntdb.Open(path)
|
||||
return &DataBase{db}, err
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// ResetUser just drop out all stickers keys for input UserID
|
||||
func (db *DataBase) ResetUser(user *tg.User) error {
|
||||
log.Ln("Trying reset all stickers of", user.ID, "user")
|
||||
return db.Update(func(tx *buntdb.Tx) error {
|
||||
var keys []string
|
||||
if err := tx.AscendKeys(
|
||||
fmt.Sprint("user:", user.ID, ":set:*"), // index
|
||||
func(key, val string) bool { // iterator
|
||||
subKeys := strings.Split(key, ":")
|
||||
if subKeys[1] == strconv.Itoa(user.ID) {
|
||||
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
|
||||
})
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// UserState return current state for UserID
|
||||
func (db *DataBase) UserState(usr *tg.User) (string, error) {
|
||||
log.Ln("Trying to get", usr.ID, "state")
|
||||
var state string
|
||||
err := DB.View(func(tx *buntdb.Tx) error {
|
||||
var err error
|
||||
state, err = tx.Get(fmt.Sprint("user:", usr.ID, ":state"))
|
||||
return err
|
||||
})
|
||||
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(usr.ID, "not found, create new one")
|
||||
if err = db.ChangeUserState(usr, models.StateNone); err != nil {
|
||||
return state, err
|
||||
}
|
||||
}
|
||||
|
||||
return state, err
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"log"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// WaitForwards is a wait group which wait send all announcements before panic
|
||||
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()
|
||||
|
||||
err = sysLogger.Crit(err.Error())
|
||||
if err != nil {
|
||||
log.Panicln(err.Error())
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
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)
|
||||
})
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
"gitlab.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
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package messages
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/actions"
|
||||
"gitlab.com/toby3d/mypackbot/internal/commands"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
tg "gitlab.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)
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package models
|
||||
|
||||
import tg "gitlab.com/toby3d/telegram"
|
||||
|
||||
// Commands... represents available and supported bot commands
|
||||
const (
|
||||
CommandAddPack = "addPack"
|
||||
CommandAddSticker = "addSticker"
|
||||
CommandCancel = "cancel"
|
||||
CommandDeleteSticker = "delSticker"
|
||||
CommandDeletePack = "delPack"
|
||||
CommandReset = "reset"
|
||||
)
|
||||
|
||||
// State... represents current state of user action
|
||||
const (
|
||||
StateNone = "none"
|
||||
StateAddSticker = CommandAddSticker
|
||||
StateAddPack = CommandAddPack
|
||||
StateDeleteSticker = CommandDeleteSticker
|
||||
StateDeletePack = CommandDeletePack
|
||||
StateReset = CommandReset
|
||||
)
|
||||
|
||||
// SetUploaded is a mimic set name of uploaded stickers without any parent set
|
||||
const SetUploaded = "?"
|
||||
|
||||
// LanguageFallback is a default language code in case what current user language
|
||||
// is not support yet
|
||||
const LanguageFallback = "en"
|
||||
|
||||
// AllowedUpdates is
|
||||
var AllowedUpdates = []string{
|
||||
tg.UpdateInlineQuery, // For searching and sending stickers
|
||||
tg.UpdateMessage, // For get commands and messages
|
||||
tg.UpdateChannelPost, // For forwarding announcements
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/config"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Channel return webhook or long polling channel with bot updates
|
||||
func Channel(webhookMode bool) (updates tg.UpdatesChannel, err error) {
|
||||
log.Ln("Preparing channel for updates...")
|
||||
if !webhookMode {
|
||||
log.Ln("Use LongPolling updates")
|
||||
|
||||
var info *tg.WebhookInfo
|
||||
info, err = bot.Bot.GetWebhookInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if info.URL != "" {
|
||||
log.Ln("Deleting webhook...")
|
||||
_, err = bot.Bot.DeleteWebhook()
|
||||
return
|
||||
}
|
||||
|
||||
updates = bot.Bot.NewLongPollingChannel(&tg.GetUpdatesParameters{
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
Timeout: 60,
|
||||
AllowedUpdates: models.AllowedUpdates,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
set, err := url.Parse(config.Config.GetString("telegram.webhook.set"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listen := config.Config.GetString("telegram.webhook.listen")
|
||||
serve := config.Config.GetString("telegram.webhook.serve")
|
||||
|
||||
log.Ln(
|
||||
"Trying set webhook on address:",
|
||||
fmt.Sprint(set.String(), bot.Bot.AccessToken),
|
||||
)
|
||||
|
||||
log.Ln("Creating new webhook...")
|
||||
params := tg.NewWebhook(fmt.Sprint(set, listen, bot.Bot.AccessToken), nil)
|
||||
params.MaxConnections = 40
|
||||
params.AllowedUpdates = models.AllowedUpdates
|
||||
|
||||
updates = bot.Bot.NewWebhookChannel(
|
||||
set,
|
||||
params, // params
|
||||
"", // certFile
|
||||
"", // keyFile
|
||||
serve, // serve
|
||||
)
|
||||
|
||||
return
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/config"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// ChannelPost checks ChannelPost update for forwarding content to bot users
|
||||
func ChannelPost(post *tg.Message) {
|
||||
channelID := config.Config.GetInt64("telegram.channel")
|
||||
if post.Chat.ID != channelID {
|
||||
log.Ln(post.Chat.ID, "!=", channelID)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := db.DB.GetUsers()
|
||||
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
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// InlineQuery checks InlineQuery updates for answer with personal results
|
||||
func InlineQuery(inlineQuery *tg.InlineQuery) {
|
||||
fixedQuery, err := utils.FixEmoji(inlineQuery.Query)
|
||||
if err == nil {
|
||||
inlineQuery.Query = fixedQuery
|
||||
}
|
||||
|
||||
answer := new(tg.AnswerInlineQueryParameters)
|
||||
answer.InlineQueryID = inlineQuery.ID
|
||||
answer.CacheTime = 1
|
||||
answer.IsPersonal = true
|
||||
|
||||
if len([]rune(inlineQuery.Query)) >= 256 {
|
||||
_, err = bot.Bot.AnswerInlineQuery(answer)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Ln("Let's preparing answer...")
|
||||
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)
|
||||
errors.Check(err)
|
||||
offset++
|
||||
|
||||
stickers, err := db.DB.GetUserStickers(
|
||||
inlineQuery.From,
|
||||
&tg.InlineQuery{
|
||||
Offset: strconv.Itoa(offset),
|
||||
Query: inlineQuery.Query,
|
||||
},
|
||||
)
|
||||
errors.Check(err)
|
||||
|
||||
if len(stickers) == 0 {
|
||||
if offset == 0 && inlineQuery.Query != "" {
|
||||
// If search stickers by emoji return 0 results
|
||||
answer.SwitchPrivateMessageText = t(
|
||||
"button_inline_nothing", map[string]interface{}{
|
||||
"Query": inlineQuery.Query,
|
||||
},
|
||||
)
|
||||
|
||||
answer.SwitchPrivateMessageParameter = tg.CommandHelp
|
||||
}
|
||||
|
||||
answer.Results = nil
|
||||
} else {
|
||||
log.Ln("STICKERS FROM REQUEST:", len(stickers))
|
||||
if len(stickers) == 50 {
|
||||
answer.NextOffset = strconv.Itoa(offset)
|
||||
log.Ln("NEXT OFFSET:", answer.NextOffset)
|
||||
}
|
||||
|
||||
var results = make([]interface{}, len(stickers))
|
||||
for i, sticker := range stickers {
|
||||
results[i] = tg.NewInlineQueryResultCachedSticker(sticker, sticker)
|
||||
}
|
||||
|
||||
answer.Results = results
|
||||
}
|
||||
|
||||
log.Ln("CacheTime:", answer.CacheTime)
|
||||
|
||||
_, err = bot.Bot.AnswerInlineQuery(answer)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/actions"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/commands"
|
||||
"gitlab.com/toby3d/mypackbot/internal/messages"
|
||||
tg "gitlab.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)
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
tg "gitlab.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")),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package utils
|
||||
|
||||
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
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
tg "gitlab.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")),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
tg "gitlab.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"), " "),
|
||||
),
|
||||
)
|
||||
}
|
57
main.go
|
@ -1,57 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/config"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/updates"
|
||||
)
|
||||
|
||||
var flagWebhook = flag.Bool(
|
||||
"webhook", false,
|
||||
"enable work via webhooks (required valid certificates)",
|
||||
)
|
||||
|
||||
// init prepare configuration and other things for successful start
|
||||
func init() {
|
||||
log.Ln("Initializing...")
|
||||
|
||||
// Preload localization strings
|
||||
err := i18n.Open("i18n/")
|
||||
errors.Check(err)
|
||||
|
||||
// Preload configuration file
|
||||
config.Open("configs/config.yaml")
|
||||
|
||||
// Open database or create new one
|
||||
db.Open("stickers.db")
|
||||
|
||||
// Create bot with credentials from config
|
||||
bot.Bot, err = bot.New(config.Config.GetString("telegram.token"))
|
||||
errors.Check(err)
|
||||
}
|
||||
|
||||
// main function is a general function for work of this bot
|
||||
func main() {
|
||||
flag.Parse() // Parse flagWebhook
|
||||
|
||||
channel, err := updates.Channel(*flagWebhook)
|
||||
errors.Check(err)
|
||||
|
||||
for update := range channel {
|
||||
log.D(update)
|
||||
switch {
|
||||
case update.IsInlineQuery():
|
||||
updates.InlineQuery(update.InlineQuery)
|
||||
case update.IsMessage():
|
||||
updates.Message(update.Message)
|
||||
case update.IsChannelPost():
|
||||
updates.ChannelPost(update.ChannelPost)
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 4.0 KiB |
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="static/favicon/mstile-150x150.png"/>
|
||||
<TileColor>#56b0ef</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
After Width: | Height: | Size: 457 B |
After Width: | Height: | Size: 874 B |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "My Stickers Pack",
|
||||
"icons": [
|
||||
{
|
||||
"src": "static/favicon/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "static/favicon/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#56b0ef",
|
||||
"background_color": "#56b0ef",
|
||||
"start_url": "https://toby3d.github.io/MyPackBot/",
|
||||
"display": "standalone"
|
||||
}
|
After Width: | Height: | Size: 4.5 KiB |
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="780.000000pt" height="780.000000pt" viewBox="0 0 780.000000 780.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,780.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M5130 7790 c-557 -69 -1053 -381 -1349 -852 -41 -65 -43 -67 -69 -56
|
||||
-16 7 -81 12 -162 12 -368 -1 -861 -181 -1219 -444 -318 -234 -562 -521 -734
|
||||
-867 -253 -505 -319 -1054 -197 -1615 25 -116 96 -328 150 -448 164 -365 444
|
||||
-715 753 -939 l77 -56 0 -415 0 -415 -67 -23 c-347 -120 -591 -466 -596 -847
|
||||
-2 -129 14 -171 73 -192 50 -17 1630 -17 1680 0 54 19 70 60 70 185 0 167 -43
|
||||
326 -127 472 -52 89 -208 244 -306 305 -43 26 -115 60 -160 76 l-82 28 -3 286
|
||||
c-2 270 -1 285 15 277 39 -20 175 -63 288 -91 401 -100 840 -93 1230 19 55 16
|
||||
108 32 118 35 16 7 17 -10 17 -266 l0 -274 -73 -27 c-151 -57 -311 -190 -412
|
||||
-343 -95 -145 -147 -318 -149 -495 -1 -122 11 -156 64 -182 32 -17 93 -18 845
|
||||
-17 600 0 819 3 845 12 54 19 70 60 70 185 0 248 -84 455 -255 628 -127 129
|
||||
-289 226 -417 249 l-38 7 0 377 0 377 83 57 c449 306 766 728 937 1247 66 199
|
||||
94 342 87 447 l-5 82 52 25 c402 197 738 571 899 1001 84 225 111 382 111 645
|
||||
0 155 -4 230 -18 305 -142 782 -731 1372 -1506 1510 -112 19 -410 28 -520 15z
|
||||
m449 -314 c283 -52 526 -164 742 -344 347 -290 553 -728 553 -1172 0 -643
|
||||
-425 -1237 -1031 -1444 -216 -74 -437 -105 -611 -86 -351 37 -655 167 -900
|
||||
384 -301 267 -478 618 -522 1039 -26 243 44 563 182 828 217 419 663 731 1138
|
||||
798 41 6 86 13 100 15 47 8 265 -3 349 -18z m-2049 -1013 c25 -31 24 -45 -7
|
||||
-198 -14 -71 -17 -138 -17 -315 0 -205 2 -236 27 -351 162 -761 738 -1318
|
||||
1507 -1455 104 -19 403 -25 522 -11 l67 7 28 -42 c39 -59 37 -115 -11 -243
|
||||
-118 -313 -264 -543 -486 -765 -165 -166 -291 -259 -491 -364 -232 -122 -444
|
||||
-187 -681 -211 -335 -33 -633 -4 -882 86 -139 51 -324 141 -438 213 -239 149
|
||||
-494 411 -638 653 -115 193 -226 468 -254 630 -56 317 -41 708 35 958 68 221
|
||||
222 525 337 667 105 129 272 293 397 390 239 186 694 374 912 377 45 1 54 -3
|
||||
73 -26z"/>
|
||||
<path d="M4720 6645 c-93 -26 -142 -56 -216 -129 -81 -81 -131 -172 -165 -301
|
||||
-29 -108 -33 -374 -6 -479 53 -212 191 -385 356 -447 69 -26 189 -35 260 -20
|
||||
127 27 254 124 341 259 25 39 48 71 51 71 3 1 26 -30 52 -67 55 -80 153 -177
|
||||
217 -215 128 -74 306 -75 453 -1 121 62 244 232 288 401 42 162 35 397 -17
|
||||
553 -29 87 -96 199 -151 252 -121 117 -306 164 -482 123 -135 -32 -248 -125
|
||||
-317 -260 -22 -41 -41 -75 -45 -75 -3 0 -14 19 -26 43 -59 121 -178 234 -291
|
||||
278 -75 28 -224 35 -302 14z m207 -344 c55 -26 113 -101 175 -229 l50 -105
|
||||
-22 -56 c-62 -154 -139 -255 -222 -291 -116 -50 -207 1 -260 146 -19 49 -22
|
||||
80 -22 189 0 111 3 138 22 186 60 152 164 212 279 160z m992 1 c79 -40 130
|
||||
-149 138 -297 6 -105 -7 -186 -43 -264 -31 -69 -57 -98 -109 -122 -53 -24 -99
|
||||
-24 -152 2 -60 29 -106 87 -170 215 -63 127 -63 121 5 261 85 174 224 260 331
|
||||
205z"/>
|
||||
<path d="M2614 4866 c-64 -20 -131 -72 -169 -129 -108 -164 -48 -380 130 -468
|
||||
41 -20 65 -24 135 -24 70 0 94 4 135 24 63 32 124 93 156 156 35 72 35 198 0
|
||||
270 -32 63 -93 125 -155 155 -60 28 -171 36 -232 16z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,50 @@
|
|||
header {
|
||||
background-color: #56b0ef !important;
|
||||
color: #ffffff !important;
|
||||
min-height: 100vh;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
header .ts.image {
|
||||
position: inherit !important;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: 2rem;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 2.5rem !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
|
||||
.valign-wrapper {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.valign {
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.ts.inverted.link.button {
|
||||
color: #ffffff;
|
||||
}
|