add conversions manager;

This commit is contained in:
naudachu 2023-06-30 21:04:40 +05:00
parent d4e42a57f5
commit 5806eb37c0
27 changed files with 263 additions and 57 deletions

View File

@ -1,5 +1,5 @@
FROM golang:alpine as app-builder
WORKDIR $GOPATH/src/ticket-creator/
WORKDIR $GOPATH/src/ticket-pimp/
COPY . .
RUN apk add git
# Static build required so that we can safely copy the binary over.
@ -8,10 +8,10 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go install -ldflags '-extldflags "-sta
FROM scratch
# the test program:
COPY --from=app-builder /go/bin/main /ticket-creator
COPY --from=app-builder /go/src/ticket-creator/cmd/.env /
COPY --from=app-builder /go/bin/main /ticket-pimp
COPY --from=app-builder /go/src/ticket-pimp/cmd/.env /
# the tls certificates:
# NB: this pulls directly from the upstream image, which already has ca-certificates:
COPY --from=alpine:latest /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT ["/ticket-creator"]
ENTRYPOINT ["/ticket-pimp"]

View File

@ -0,0 +1,42 @@
package controller
import (
"io"
d "ticket-pimp/bot/domain"
"ticket-pimp/bot/ext"
)
type WorkflowController struct {
iGit ext.IGit
iCloud ext.ICloud
iYouTrack ext.IYouTrack
iCoda ext.ICoda
}
func NewWorkflowController(
gitBaseURL,
gitToken,
cloudBaseURL,
cloudAuthUser,
cloudAuthPass,
ytBaseURL,
ytToken string,
) *WorkflowController {
return &WorkflowController{
iGit: ext.NewGit(gitBaseURL, gitToken),
iCloud: ext.NewCloud(cloudBaseURL, cloudAuthUser, cloudAuthPass),
iYouTrack: ext.NewYT(ytBaseURL, ytToken),
iCoda: ext.NewCodaClient(),
}
}
type IWorkflowController interface {
Workflow(name string) (string, error)
CreateRepo(name string) (*d.Git, error)
CreateFolder(name string) (*d.Folder, error)
NewTask(summ, desc, c, cLink string) *Task
CreateTask(t *Task) (*Task, error)
ThrowConversions(f io.ReadCloser, appID string, token string) *d.ConversionLog
}

View File

@ -0,0 +1,55 @@
package controller
import (
"encoding/csv"
"io"
"strings"
"github.com/imroc/req/v3"
d "ticket-pimp/bot/domain"
)
func (wc *WorkflowController) ThrowConversions(f io.ReadCloser, appID string, token string) *d.ConversionLog {
c := req.C().
SetBaseURL("https://graph.facebook.com/v15.0/").
DevMode()
const currency = "USD"
r := csv.NewReader(f)
conversionLog := d.ConversionLog{}
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
return nil
}
advertiser := strings.Split(record[0], ";")[0]
params := map[string]string{
"advertiser_id": advertiser,
"event": "CUSTOM_APP_EVENTS",
"application_tracking_enabled": "1",
"advertiser_tracking_enabled": "1",
"custom_events": `[{"_eventName":"fb_mobile_purchase"}]`,
}
res, _ := c.R().
SetQueryString(token).
SetQueryParams(params).
Post(appID + "/activities")
if res.Err != nil {
conversionLog.Advertiser = append(conversionLog.Advertiser, advertiser)
}
}
return &conversionLog
}

View File

@ -0,0 +1,33 @@
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var a1 = data[1][1];
var a2 = ''
var a3 = data[1][2];
var reqUrl = data[1][3];
var startarray = data[1][4]
var regCount = 0;
var depCount = 0;
// reqUrl = 'https://graph.facebook.com/v15.0/841990173617973/activities?event=CUSTOM_APP_EVENTS&application_tracking_enabled=1&advertiser_tracking_enabled=1&advertiser_id=0a42bbee-c049-4180-ae3f-ce76ff33556e&841990173617973%7Cnjqit85G_uma0voikeP6eJvBiQk&custom_events=[%7B%22_eventName%22:%22fb_mobile_purchase%22%7D]'
reqUrl = reqUrl.replace('A1', a1)
reqUrl = reqUrl.replace('A3', a3)
Logger.log("urll " + reqUrl)
Logger.log("urll " + encodeURI(reqUrl).toString())
for (var i = startarray; i < data.length; i++) {
a2 = data[i][0]
reqUrl = reqUrl.replace('A2', a2)
var params = {
"method": 'post'
}
var response = UrlFetchApp.fetch(encodeURI(reqUrl), params);
Logger.log(i + " " + response.getContentText() + ' ' + response.getResponseCode());
depCount++;
}
Logger.log('Reg Count= ' + regCount);
Logger.log('Dep Count= ' + depCount);
}

View File

@ -1,6 +1,6 @@
package controller
import d "ticket-pimp/domain"
import d "ticket-pimp/bot/domain"
func (wc *WorkflowController) CreateFolder(name string) (*d.Folder, error) {

View File

@ -1,6 +1,6 @@
package controller
import d "ticket-pimp/domain"
import d "ticket-pimp/bot/domain"
func (wc *WorkflowController) CreateRepo(name string) (*d.Git, error) {
//Create git repository with iGit interface;

View File

@ -3,43 +3,9 @@ package controller
import (
"fmt"
"sync"
d "ticket-pimp/domain"
"ticket-pimp/ext"
d "ticket-pimp/bot/domain"
)
type WorkflowController struct {
iGit ext.IGit
iCloud ext.ICloud
iYouTrack ext.IYouTrack
iCoda ext.ICoda
}
func NewWorkflowController(
gitBaseURL,
gitToken,
cloudBaseURL,
cloudAuthUser,
cloudAuthPass,
ytBaseURL,
ytToken string,
) *WorkflowController {
return &WorkflowController{
iGit: ext.NewGit(gitBaseURL, gitToken),
iCloud: ext.NewCloud(cloudBaseURL, cloudAuthUser, cloudAuthPass),
iYouTrack: ext.NewYT(ytBaseURL, ytToken),
iCoda: ext.NewCodaClient(),
}
}
type IWorkflowController interface {
Workflow(name string) (string, error)
CreateRepo(name string) (*d.Git, error)
CreateFolder(name string) (*d.Folder, error)
NewTask(summ, desc, c, cLink string) *Task
CreateTask(t *Task) (*Task, error)
}
func (wc *WorkflowController) Workflow(name string) (string, error) {
yt := wc.iYouTrack

5
bot/domain/conversion.go Normal file
View File

@ -0,0 +1,5 @@
package domain
type ConversionLog struct {
Advertiser []string
}

View File

@ -4,8 +4,8 @@ import (
"fmt"
"os"
"strconv"
d "ticket-pimp/domain"
"ticket-pimp/helpers"
d "ticket-pimp/bot/domain"
"ticket-pimp/bot/helpers"
"time"
)

View File

@ -3,8 +3,8 @@ package ext
import (
"log"
"os"
"ticket-pimp/domain"
"ticket-pimp/helpers"
"ticket-pimp/bot/domain"
"ticket-pimp/bot/helpers"
"time"
)

View File

@ -5,7 +5,7 @@ import (
"log"
"time"
d "ticket-pimp/domain"
d "ticket-pimp/bot/domain"
"github.com/imroc/req/v3"
)

View File

@ -4,9 +4,10 @@ import (
"context"
"errors"
"fmt"
"log"
"strings"
"ticket-pimp/controller"
d "ticket-pimp/domain"
"ticket-pimp/bot/controller"
d "ticket-pimp/bot/domain"
"github.com/mr-linch/go-tg"
"github.com/mr-linch/go-tg/tgb"
@ -30,6 +31,7 @@ func NewHandler(gitBaseURL, gitToken, cloudBaseURL, cloudAuthUser, cloudAuthPass
}
func (h *Handler) PingHandler(ctx context.Context, mu *tgb.MessageUpdate) error {
return mu.Answer("pong").DoVoid(ctx)
}
@ -51,7 +53,7 @@ func newGit(d *d.Git) *git {
}
// FYI: Telegram doesn't renders this hyperlink, if the url is localhost 🤷‍♂️
func (g *git) prepareAnswer() string {
func (g *git) PrepareAnswer() string {
return tg.HTML.Text(
tg.HTML.Line(
"Repo ",
@ -76,7 +78,7 @@ func (h *Handler) NewRepoHandler(ctx context.Context, mu *tgb.MessageUpdate) err
return mu.Answer(errorAnswer(err.Error())).ParseMode(tg.HTML).DoVoid(ctx)
}
resp := newGit(g).prepareAnswer()
resp := newGit(g).PrepareAnswer()
return mu.Answer(resp).ParseMode(tg.HTML).DoVoid(ctx)
}
@ -175,3 +177,51 @@ func (h *Handler) NewTaskHandler(ctx context.Context, mu *tgb.MessageUpdate) err
),
)).ParseMode(tg.HTML).DoVoid(ctx)
}
func (h *Handler) NewConversion(ctx context.Context, mu *tgb.MessageUpdate) error {
msg := strings.TrimSpace(strings.Replace(mu.Caption, "/conversion", "", 1))
appID, token := normalizeToken(msg)
fid := mu.Update.Message.Document.FileID
client := mu.Client
file, err := client.GetFile(fid).Do(ctx)
if err != nil {
return err
}
f, err := client.Download(ctx, file.FilePath)
if err != nil {
return err
}
defer f.Close()
l := h.workflow.ThrowConversions(f, appID, token)
if len(l.Advertiser) != 0 {
return mu.Answer(tg.HTML.Text(
"Неуспешные запросы:",
tg.HTML.Code(strings.Join(l.Advertiser, ", ")),
)).ParseMode(tg.HTML).DoVoid(ctx)
}
return mu.Answer(tg.HTML.Text(
"Конверсии отправлены",
)).ParseMode(tg.HTML).DoVoid(ctx)
}
func normalizeToken(msg string) (string, string) {
msg = strings.TrimSpace(msg)
args := strings.Split(msg, "|")
if len(args) != 2 {
log.Print(len(args))
return "", ""
}
return args[0], args[0] + "|" + args[1]
}

View File

@ -0,0 +1,34 @@
package handler
import (
"testing"
"ticket-pimp/bot/domain"
)
type test struct {
arg domain.Git
expected string
}
var tests = []test{
{domain.Git{
Name: "text",
FullName: "",
Private: false,
Url: "",
CloneUrl: "",
HtmlUrl: "https://reddit.com/",
SshUrl: "",
}, "Repo <a href=\"https://reddit.com/\">text</a> has been created!"},
}
func TestPrepareAnswer(t *testing.T) {
for _, test := range tests {
g := newGit(&test.arg)
if output := g.PrepareAnswer(); output != test.expected {
t.Errorf("Output %q not equal to expected %q", output, test.expected)
}
}
}

View File

@ -7,7 +7,7 @@ import (
"os"
"os/signal"
"syscall"
"ticket-pimp/handler"
"ticket-pimp/bot/handler"
"github.com/joho/godotenv"
"github.com/mr-linch/go-tg"
@ -22,20 +22,27 @@ func main() {
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
defer cancel()
if err := run(ctx); err != nil {
if err := runBot(ctx); err != nil {
fmt.Println(err)
defer os.Exit(1)
}
}
// env
// env function reads provided file and setup envirmental variables;
func env(envFilePath string) {
err := godotenv.Load(envFilePath)
if err != nil {
log.Fatal("Error loading .env file")
log.Fatal("Error while loading env file")
}
}
func run(ctx context.Context) error {
// runBot ...
// ..function creates new Telegram BOT instance
// ..throw env variables through bot's handlers
// ..setup tg bot router;
// and finally returns tgb.Poller
func runBot(ctx context.Context) error {
client := tg.New(os.Getenv("TG_API"))
@ -54,7 +61,8 @@ func run(ctx context.Context) error {
Message(h.PingHandler, tgb.Command("ping")).
Message(h.NewRepoHandler, tgb.TextHasPrefix("/repo")).
Message(h.NewFolderHandler, tgb.TextHasPrefix("/folder")).
Message(h.NewTaskHandler, tgb.TextHasPrefix("/task"))
Message(h.NewTaskHandler, tgb.TextHasPrefix("/task")).
Message(h.NewConversion, tgb.TextHasPrefix("/conversion"))
return tgb.NewPoller(
router,

4
go.mod
View File

@ -13,6 +13,7 @@ require (
github.com/artsafin/coda-go-client v1.0.4 // indirect
github.com/bytedance/sonic v1.9.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deepmap/oapi-codegen v1.13.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
@ -38,10 +39,13 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.10.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/quic-go/quic-go v0.35.1 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/testify v1.8.4 // indirect
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 // indirect
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect

2
go.sum
View File

@ -107,6 +107,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2 h1:VsBj3UD2xyAOu7kJw6O/2jjG2UXLFoBzihqDU9Ofg9M=
github.com/studio-b12/gowebdav v0.0.0-20230203202212-3282f94193f2/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=

View File

@ -1,5 +1,12 @@
# Сборка и запуск:
Первые шаги делаю на локальной машине:
1. Поменять в коде файл окружения на '.env'
2. Собрать контейнер: `docker build -t naudachu/ticket-pimp:latest --pull .`
3. Затолкать контейнер в docker hub: `docker push naudachu/ticket-pimp:latest`
`docker build -t golang-scratch-test --pull .`
Далее с сервера:
1. Вытягиваем новый образ: `docker pull naudachu/ticket-pimp`
2. Запускаем в фоне: `docker run -d naudachu/ticket-pimp`
# To-do P1:
@ -22,4 +29,4 @@
- [ ] Складывать в описание репозитория ссылку на тикет;
- [ ] Сделать базулю с достойными пользователями;
- [x] Run bot on docker scratch: https://github.com/jeremyhuiskamp/golang-docker-scratch/blob/main/README.md
- [x] Run bot on docker scratch: https://github.com/jeremyhuiskamp/golang-docker-scratch/blob/main/README.mdа