unhardcode bot username, ci/cd
This commit is contained in:
parent
e0ba5347a1
commit
86ba26f559
20
.drone.yml
Normal file
20
.drone.yml
Normal file
@ -0,0 +1,20 @@
|
||||
kind: pipeline
|
||||
type: exec
|
||||
name: default
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: build and push
|
||||
commands:
|
||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||
- make docker_release
|
||||
when:
|
||||
branch:
|
||||
- master
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
18
Dockerfile
Normal file
18
Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
FROM golang:1.22-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY ./ ./
|
||||
RUN set -eux; \
|
||||
apk add --no-cache bash make git dumb-init mailcap ca-certificates tzdata; \
|
||||
update-ca-certificates; \
|
||||
make
|
||||
|
||||
FROM scratch
|
||||
WORKDIR /
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
COPY --from=builder /etc/mime.types /etc/mime.types
|
||||
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
COPY --from=builder /usr/bin/dumb-init /bin/dumb-init
|
||||
COPY --from=builder /app/build/vegapokerbot /vegapokerbot
|
||||
EXPOSE 3333
|
||||
ENTRYPOINT ["/bin/dumb-init", "--"]
|
||||
CMD ["/vegapokerbot", "run"]
|
7
Makefile
7
Makefile
@ -5,12 +5,19 @@ ROOT_DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
BIN=$(ROOT_DIR)/build/vegapokerbot
|
||||
GO_VERSION=$(shell go version | sed -e 's/go version //')
|
||||
BIN_DIR=$(ROOT_DIR)/build
|
||||
APP_IMAGE=gitea.neur0tx.site/neur0toxine/vegapokerbot:latest
|
||||
|
||||
build: deps fmt
|
||||
@echo "> building with ${GO_VERSION}"
|
||||
@CGO_ENABLED=0 go build -buildvcs=false -tags=release -o $(BIN) .
|
||||
@echo $(BIN)
|
||||
|
||||
docker:
|
||||
@docker buildx build --platform linux/amd64 --tag ${APP_IMAGE} --file Dockerfile .
|
||||
|
||||
docker_release:
|
||||
@docker buildx build --push --platform linux/amd64 --tag ${APP_IMAGE} --file Dockerfile .
|
||||
|
||||
run:
|
||||
@${BIN} run
|
||||
fmt:
|
||||
|
27
compose.prod.yml
Normal file
27
compose.prod.yml
Normal file
@ -0,0 +1,27 @@
|
||||
services:
|
||||
db:
|
||||
image: postgres:latest
|
||||
environment:
|
||||
POSTGRES_USER: app
|
||||
POSTGRES_PASSWORD: app
|
||||
POSTGRES_DATABASE: app
|
||||
networks:
|
||||
- default
|
||||
|
||||
app:
|
||||
image: "gitea.neur0tx.site/neur0toxine/vegapokerbot:latest"
|
||||
networks:
|
||||
- default
|
||||
links:
|
||||
- db
|
||||
env_file:
|
||||
- .env
|
||||
labels:
|
||||
traefik.enable: "true"
|
||||
traefik.http.routers.vegapokerbot.entrypoints: web
|
||||
traefik.http.routers.vegapokerbot.rule: "Host(`example.com`) && PathPrefix(`/webhook`)"
|
||||
traefik.http.services.vegapokerbot.loadbalancer.server.port: 3333
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
@ -18,9 +18,7 @@ type Config struct {
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
var cfg Config
|
||||
if err := godotenv.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = godotenv.Load()
|
||||
loader := aconfig.LoaderFor(&cfg, aconfig.Config{
|
||||
SkipFlags: true,
|
||||
EnvPrefix: "POKER_BOT",
|
||||
|
@ -367,7 +367,7 @@ func (h *CallbackQueryHandler) handleAnswerRedmine(answer util.Answer, chatID in
|
||||
MessageID: msgID,
|
||||
Text: loc.Message("redmine_will_not_be_configured"),
|
||||
})
|
||||
return util.SendSetupDone(h.App.TG(), user.ChatID, loc)
|
||||
return util.SendSetupDone(h.App.TG(), h.App.TGProfile().Username, user.ChatID, loc)
|
||||
}
|
||||
_, err := h.App.TG().EditMessageText(&telego.EditMessageTextParams{
|
||||
ChatID: tu.ID(user.ChatID),
|
||||
@ -428,7 +428,7 @@ func (h *CallbackQueryHandler) handleAnswerRedmineSendResults(answer util.Answer
|
||||
MessageID: msgID,
|
||||
Text: loc.Message("redmine_poker_will_not_be_configured"),
|
||||
})
|
||||
return util.SendSetupDone(h.App.TG(), user.ChatID, loc)
|
||||
return util.SendSetupDone(h.App.TG(), h.App.TGProfile().Username, user.ChatID, loc)
|
||||
}
|
||||
_, err := h.App.TG().EditMessageText(&telego.EditMessageTextParams{
|
||||
ChatID: tu.ID(user.ChatID),
|
||||
|
@ -53,7 +53,8 @@ func (h *ChatMemberUpdatedHandler) handleAddToChat(tgChat telego.Chat) error {
|
||||
if user == nil || user.ID == 0 {
|
||||
_, err = h.App.TG().SendMessage(&telego.SendMessageParams{
|
||||
ChatID: tu.ID(tgChat.ID),
|
||||
Text: h.Localizer(language.English.String()).Message("you_should_register_first"),
|
||||
Text: h.Localizer(language.English.String()).
|
||||
Template("you_should_register_first", map[string]interface{}{"Name": h.App.TGProfile().Username}),
|
||||
ParseMode: telego.ModeMarkdown,
|
||||
})
|
||||
h.leaveChat(tgChat.ID)
|
||||
|
@ -21,23 +21,27 @@ func (h *MessageHandler) Handle(wh telego.Update) error {
|
||||
if wh.Message.From == nil {
|
||||
return nil
|
||||
}
|
||||
if wh.Message.Chat.Type == telego.ChatTypePrivate {
|
||||
|
||||
if util.MatchCommand("start", wh.Message) {
|
||||
return wizard.NewRegister(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
||||
}
|
||||
if util.MatchCommand("help", wh.Message) {
|
||||
return wizard.NewHelpCommand(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
||||
}
|
||||
|
||||
setup, found := store.RedmineSetups.Get(wh.Message.Chat.ID)
|
||||
if found {
|
||||
return wizard.NewRedmineSetup(h.App, wh.Message.From.ID, wh.Message.Chat.ID, setup).Handle(wh)
|
||||
}
|
||||
}
|
||||
|
||||
if util.MatchCommand("poll", wh.Message) {
|
||||
return group.NewPoll(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
||||
}
|
||||
|
||||
if util.MatchCommand("start", wh.Message) {
|
||||
return wizard.NewRegister(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
||||
}
|
||||
|
||||
setup, found := store.RedmineSetups.Get(wh.Message.Chat.ID)
|
||||
if found {
|
||||
return wizard.NewRedmineSetup(h.App, wh.Message.From.ID, wh.Message.Chat.ID, setup).Handle(wh)
|
||||
}
|
||||
|
||||
if util.MatchCommand("help", wh.Message) {
|
||||
return wizard.NewHelpCommand(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
||||
}
|
||||
|
||||
return wizard.NewUnknownCommand(h.App, wh.Message.From.ID, wh.Message.Chat.ID).Handle(wh)
|
||||
}
|
||||
|
@ -6,10 +6,10 @@ import (
|
||||
tu "github.com/mymmrac/telego/telegoutil"
|
||||
)
|
||||
|
||||
func SendSetupDone(tg *telego.Bot, chatID int64, loc locale.Localizer) error {
|
||||
func SendSetupDone(tg *telego.Bot, username string, chatID int64, loc locale.Localizer) error {
|
||||
_, err := tg.SendMessage(&telego.SendMessageParams{
|
||||
ChatID: tu.ID(chatID),
|
||||
Text: loc.Message("setup_done"),
|
||||
Text: loc.Template("setup_done", map[string]interface{}{"Name": username}),
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ func NewHelpCommand(app iface.App, userID, chatID int64) *HelpCommand {
|
||||
func (h *HelpCommand) Handle(wh telego.Update) error {
|
||||
_, err := h.App.TG().SendMessage(&telego.SendMessageParams{
|
||||
ChatID: tu.ID(wh.Message.Chat.ID),
|
||||
Text: h.Localizer(wh.Message.From.LanguageCode).Message("help_output"),
|
||||
Text: h.Localizer(wh.Message.From.LanguageCode).
|
||||
Template("help_output", map[string]interface{}{"Name": h.App.TGProfile().Username}),
|
||||
ParseMode: telego.ModeMarkdown,
|
||||
})
|
||||
return err
|
||||
|
@ -27,6 +27,9 @@ func NewRedmineSetup(app iface.App, userID, chatID int64, config *store.RedmineS
|
||||
}
|
||||
|
||||
func (h *RedmineSetup) Handle(wh telego.Update) error {
|
||||
if wh.Message.Chat.Type != telego.ChatTypePrivate {
|
||||
return nil
|
||||
}
|
||||
if h.Redmine == nil {
|
||||
return nil
|
||||
}
|
||||
@ -119,7 +122,7 @@ func (h *RedmineSetup) handleKeyStep(text string, loc locale.Localizer) error {
|
||||
|
||||
_, _ = h.App.TG().SendMessage(&telego.SendMessageParams{
|
||||
ChatID: tu.ID(h.ChatID),
|
||||
Text: loc.Message("redmine_was_connected"),
|
||||
Text: loc.Template("redmine_was_connected", map[string]interface{}{"Name": h.App.TGProfile().Username}),
|
||||
ParseMode: telego.ModeMarkdown,
|
||||
})
|
||||
_, err = h.App.TG().SendMessage(&telego.SendMessageParams{
|
||||
@ -178,7 +181,7 @@ func (h *RedmineSetup) handleSPFieldStep(text string, loc locale.Localizer) erro
|
||||
ParseMode: telego.ModeMarkdown,
|
||||
})
|
||||
store.RedmineSetups.Delete(h.ChatID)
|
||||
return util.SendSetupDone(h.App.TG(), h.ChatID, loc)
|
||||
return util.SendSetupDone(h.App.TG(), h.App.TGProfile().Username, h.ChatID, loc)
|
||||
}
|
||||
|
||||
func (h *RedmineSetup) processURL(input string) string {
|
||||
|
@ -16,6 +16,10 @@ func NewRegister(app iface.App, userID, chatID int64) *Register {
|
||||
}
|
||||
|
||||
func (h *Register) Handle(wh telego.Update) error {
|
||||
if wh.Message.Chat.Type != telego.ChatTypePrivate {
|
||||
return nil
|
||||
}
|
||||
|
||||
loc := h.Localizer(wh.Message.From.LanguageCode)
|
||||
userRepo := h.App.DB().ForUser()
|
||||
user, err := userRepo.ByTelegramID(wh.Message.From.ID)
|
||||
|
@ -1,5 +1,5 @@
|
||||
welcome: "👋 Hello! This bot allows you to conduct Scrum Poker directly in a Telegram chat and pass results to issues tied to a Redmine instance. To start, add the bot to the chat where you want to conduct poker. After this, you can continue with the setup."
|
||||
you_should_register_first: "You must register in the @vegapokerbot first before adding bot to the group."
|
||||
you_should_register_first: "You must register in the @{{.Name}} first before adding bot to the group."
|
||||
too_many_members_in_the_group: "Bot doesn't support groups with more than {{.Limit}} members."
|
||||
internal_error: "❌ Internal error, try again later."
|
||||
bot_was_added: "Great, the bot has been added to the group *{{.Name}}*."
|
||||
@ -8,7 +8,7 @@ standard_vote_keyboard: "⌨️ Standard poker keyboard"
|
||||
sp_vote_keyboard: "⌨️ Story Points poker keyboard"
|
||||
bot_was_removed_from_group: "⚠️ Bot was removed from group *{{.Name}}*. Group parameters have been deleted. You can restore connection with the group by adding the bot to the group and performing initial setup again."
|
||||
unknown_command: "❔ Unknown command. Please use /help to check the list of available commands."
|
||||
help_output: "📄 Available commands:\n\n• /start - registers you in the bot;\n• /poll@vegapokerbot <task> - starts the poker in group, works only in the connected groups;\n• /help - prints this message."
|
||||
help_output: "📄 Available commands:\n\n• /start - registers you in the bot;\n• /poll@{{.Name}} <task> - starts the poker in group, works only in the connected groups;\n• /help - prints this message."
|
||||
choose_at_least_one: "❌ You must choose at least one participant."
|
||||
ask_for_redmine: "📕 Configure integration with Redmine?"
|
||||
redmine_will_not_be_configured: "👌 Redmine will not be configured. You won't be able to send poker results to Redmine or load additional task information."
|
||||
@ -21,11 +21,11 @@ use_this_command_in_group: "😒 This command should only be used in your group.
|
||||
please_send_redmine_url: "Please send the URL of your Redmine instance."
|
||||
please_send_redmine_key: "Please send your API key that will be used by the bot to interact with Redmine. The bot will perform the following actions:\n\n• retrieving task data;\n• writing the result of the poker in the task (if configured in subsequent steps).\n\nThe access key for the API can be found on the left part of the page by [this link]({{.Origin}}/my/account)."
|
||||
invalid_redmine_credentials: "⚠️ Failed to verify connection with Redmine. Will try setting up again?"
|
||||
redmine_was_connected: "✔️ Great, Redmine is now connected! Now when starting a vote and passing it on to /poll@vegapokerbot in the chat, the bot will mention the task theme and generate a link to the task.\n\nThe bot can pass results of the vote directly into custom fields of issues in Redmine. The following data are passed:\n\n• the vote result;\n• the vote result converted to hours (assuming that the vote result is story points, conversion to hours is automatic)."
|
||||
redmine_was_connected: "✔️ Great, Redmine is now connected! Now when starting a vote and passing it on to /poll@{{.Name}} in the chat, the bot will mention the task theme and generate a link to the task.\n\nThe bot can pass results of the vote directly into custom fields of issues in Redmine. The following data are passed:\n\n• the vote result;\n• the vote result converted to hours (assuming that the vote result is story points, conversion to hours is automatic)."
|
||||
should_send_poker_to_redmine: "🕑 Do you want to send poker results to Redmine? You will need to specify custom field name which will be used to send poker results."
|
||||
specify_result_field: "Please specify the name of the field where the poker result will be written."
|
||||
should_send_estimated_hours_redmine: "🕑 Do you want the poker result to be converted to hours and also sent to Redmine?\n\nBot will assume that you treat the vote result as story points. In that case the result will be multiplied by 8 before sending it to Redmine. Example: vote result 1.5 means that we will send to Redmine 1.5 ✖️ 8 = 12."
|
||||
setup_done: "✔️ Done, now your chat is connected to the bot!\nUse the command /poll@vegapokerbot in the connected chat to start a vote."
|
||||
setup_done: "✔️ Done, now your chat is connected to the bot!\nUse the command /poll@{{.Name}} in the connected chat to start a vote."
|
||||
yes: "✔️ Yes"
|
||||
no: "✖️ No"
|
||||
poker_start: "@{{.Name}} started poker{{.DotOrColon}}{{.Subject}}"
|
||||
|
@ -1,5 +1,5 @@
|
||||
welcome: "👋 Привет! Этот бот позволяет проводить Scrum Poker прямо в чате Telegram и передавать результаты в задачи в привязанном Redmine. Для начала добавьте бота в чат, в котором вы хотите проводить poker. После этого вы сможете продолжить настройку."
|
||||
you_should_register_first: "Перед добавлением бота в группу необходимо зарегистрироваться в @vegapokerbot."
|
||||
you_should_register_first: "Перед добавлением бота в группу необходимо зарегистрироваться в @{{.Name}}."
|
||||
too_many_members_in_the_group: "Бот не поддерживает группы, в которых количество участников превышает {{.Limit}}."
|
||||
internal_error: "❌ Внутренняя ошибка, попробуйте попытку позже."
|
||||
bot_was_added: "Отлично, бот был добавлен в группу *{{.Name}}*."
|
||||
@ -8,7 +8,7 @@ standard_vote_keyboard: "⌨️ Стандартная poker-клавиатур
|
||||
sp_vote_keyboard: "⌨️ Story Points poker-клавиатура"
|
||||
bot_was_removed_from_group: "⚠️ Бот был удален из группы *{{.Name}}*. Настройки группы были удалены. Для того, чтобы восстановить связь с группой добавьте бота в нее и произведите настройку повторно."
|
||||
unknown_command: "❔ Неизвестная команда. Используйте /help для вывода списка доступных команд."
|
||||
help_output: "📄 Доступные команды:\n\n• /start - регистрация в боте;\n• /poll@vegapokerbot <task> - запускает poker в группе, работает только в подключенных группах;\n• /help - выводит эту справку."
|
||||
help_output: "📄 Доступные команды:\n\n• /start - регистрация в боте;\n• /poll@{{.Name}} <task> - запускает poker в группе, работает только в подключенных группах;\n• /help - выводит эту справку."
|
||||
choose_at_least_one: "❌ Вы должны выбрать хотя бы одного участника."
|
||||
ask_for_redmine: "📕 Настроить интеграцию с Redmine?"
|
||||
send_result_redmine: "📕 Отправить результат в Redmine"
|
||||
@ -22,11 +22,11 @@ use_this_command_in_group: "😒 Эта команда должна исполь
|
||||
please_send_redmine_url: "Пожалуйста, отправьте ссылку на свой инстанс Redmine."
|
||||
please_send_redmine_key: "Отправьте свой API-ключ, который будет использоваться ботом для взаимодействия с Redmine. Бот будет выполнять следующие действия:\n\n• получение данных задачи;\n• запись результата покера в задачу (если будет настроено в следующих шагах).\n\nКлюч доступа к API можно найти в левой части страницы по [этой ссылке]({{.Origin}}/my/account)."
|
||||
invalid_redmine_credentials: "⚠️ Не удалось проверить связь с Redmine. Будете пробовать настроить заново?"
|
||||
redmine_was_connected: "✔️ Отлично, теперь Redmine подключен! Теперь при начале голосования и передаче в команду /poll@vegapokerbot номера задачи бот укажет в сообщении тему задачи и сгенерирует ссылку на задачу.\n\nБот может передавать результаты голосования напрямую в поля задачи в Redmine. Передаются следующие данные:\n\n• полученная в итоге голосования оценка;\n• конвертированная в часы оценка (если оценка в story points, конвертация в часы автоматическая)."
|
||||
redmine_was_connected: "✔️ Отлично, теперь Redmine подключен! Теперь при начале голосования и передаче в команду /poll@{{.Name}} номера задачи бот укажет в сообщении тему задачи и сгенерирует ссылку на задачу.\n\nБот может передавать результаты голосования напрямую в поля задачи в Redmine. Передаются следующие данные:\n\n• полученная в итоге голосования оценка;\n• конвертированная в часы оценка (если оценка в story points, конвертация в часы автоматическая)."
|
||||
should_send_poker_to_redmine: "🕑 Передавать в Redmine результат проведения poker без конвертации в часы? Потребуется указать название кастомного поля для передачи данных."
|
||||
specify_result_field: "Укажите название поля, в которое будет передаваться результат оценки."
|
||||
should_send_estimated_hours_redmine: "🕑 Передавать в поле *Оценка временных затрат* сконвертированный в часы результат оценки?\n\nПредполагается, что оценка - это story points, поэтому будет передаваться умноженный на 8 результат. Пример: результат голосования 1,5 - в дополнительное поле передастся 1,5 ✖️ 8 = 12."
|
||||
setup_done: "✔️ Готово, чат подключен к боту!\nИспользуйте команду /poll@vegapokerbot в подключенном чате для запуска голосования."
|
||||
setup_done: "✔️ Готово, чат подключен к боту!\nИспользуйте команду /poll@{{.Name}} в подключенном чате для запуска голосования."
|
||||
yes: "✔️ Да"
|
||||
no: "✖️ Нет"
|
||||
poker_start: "@{{.Name}} запустил покер{{.DotOrColon}}{{.Subject}}"
|
||||
|
Loading…
Reference in New Issue
Block a user