diff --git a/Dockerfile b/Dockerfile index 3842796..53054ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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"] \ No newline at end of file +ENTRYPOINT ["/ticket-pimp"] \ No newline at end of file diff --git a/bot/controller/controller.go b/bot/controller/controller.go new file mode 100644 index 0000000..40e1dd2 --- /dev/null +++ b/bot/controller/controller.go @@ -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 +} diff --git a/bot/controller/conversions.go b/bot/controller/conversions.go new file mode 100644 index 0000000..b8abf2a --- /dev/null +++ b/bot/controller/conversions.go @@ -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 +} diff --git a/bot/controller/conversions.js b/bot/controller/conversions.js new file mode 100644 index 0000000..acf6ade --- /dev/null +++ b/bot/controller/conversions.js @@ -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); + + } diff --git a/controller/folder.go b/bot/controller/folder.go similarity index 90% rename from controller/folder.go rename to bot/controller/folder.go index 728b9b3..e280892 100644 --- a/controller/folder.go +++ b/bot/controller/folder.go @@ -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) { diff --git a/controller/git.go b/bot/controller/git.go similarity index 91% rename from controller/git.go rename to bot/controller/git.go index af2e9b9..2113ebd 100644 --- a/controller/git.go +++ b/bot/controller/git.go @@ -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; diff --git a/controller/task.go b/bot/controller/task.go similarity index 100% rename from controller/task.go rename to bot/controller/task.go diff --git a/controller/controller.go b/bot/controller/ticket-workflow.go similarity index 53% rename from controller/controller.go rename to bot/controller/ticket-workflow.go index 63b5fa0..7b7cc6f 100644 --- a/controller/controller.go +++ b/bot/controller/ticket-workflow.go @@ -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 diff --git a/domain/cloud.go b/bot/domain/cloud.go similarity index 100% rename from domain/cloud.go rename to bot/domain/cloud.go diff --git a/bot/domain/conversion.go b/bot/domain/conversion.go new file mode 100644 index 0000000..1931882 --- /dev/null +++ b/bot/domain/conversion.go @@ -0,0 +1,5 @@ +package domain + +type ConversionLog struct { + Advertiser []string +} diff --git a/domain/git.go b/bot/domain/git.go similarity index 100% rename from domain/git.go rename to bot/domain/git.go diff --git a/domain/task.go b/bot/domain/task.go similarity index 100% rename from domain/task.go rename to bot/domain/task.go diff --git a/domain/youtrack.go b/bot/domain/youtrack.go similarity index 100% rename from domain/youtrack.go rename to bot/domain/youtrack.go diff --git a/ext/client.go b/bot/ext/client.go similarity index 100% rename from ext/client.go rename to bot/ext/client.go diff --git a/ext/cloud.go b/bot/ext/cloud.go similarity index 97% rename from ext/cloud.go rename to bot/ext/cloud.go index 3d111ff..748ea77 100644 --- a/ext/cloud.go +++ b/bot/ext/cloud.go @@ -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" ) diff --git a/ext/coda.go b/bot/ext/coda.go similarity index 100% rename from ext/coda.go rename to bot/ext/coda.go diff --git a/ext/git.go b/bot/ext/git.go similarity index 96% rename from ext/git.go rename to bot/ext/git.go index 7b47657..0d8d967 100644 --- a/ext/git.go +++ b/bot/ext/git.go @@ -3,8 +3,8 @@ package ext import ( "log" "os" - "ticket-pimp/domain" - "ticket-pimp/helpers" + "ticket-pimp/bot/domain" + "ticket-pimp/bot/helpers" "time" ) diff --git a/ext/yt.go b/bot/ext/yt.go similarity index 99% rename from ext/yt.go rename to bot/ext/yt.go index 1213c9d..a3df01a 100644 --- a/ext/yt.go +++ b/bot/ext/yt.go @@ -5,7 +5,7 @@ import ( "log" "time" - d "ticket-pimp/domain" + d "ticket-pimp/bot/domain" "github.com/imroc/req/v3" ) diff --git a/handler/handler.go b/bot/handler/handler.go similarity index 75% rename from handler/handler.go rename to bot/handler/handler.go index 3c3a691..ff4937d 100644 --- a/handler/handler.go +++ b/bot/handler/handler.go @@ -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] + +} diff --git a/bot/handler/handler_test.go b/bot/handler/handler_test.go new file mode 100644 index 0000000..79993a9 --- /dev/null +++ b/bot/handler/handler_test.go @@ -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 text 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) + } + } +} diff --git a/helpers/helpers.go b/bot/helpers/helpers.go similarity index 100% rename from helpers/helpers.go rename to bot/helpers/helpers.go diff --git a/helpers/helpers_test.go b/bot/helpers/helpers_test.go similarity index 100% rename from helpers/helpers_test.go rename to bot/helpers/helpers_test.go diff --git a/helpers/xml_test.go b/bot/helpers/xml_test.go similarity index 100% rename from helpers/xml_test.go rename to bot/helpers/xml_test.go diff --git a/cmd/main.go b/cmd/main.go index e4cae3e..b0f327a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -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, diff --git a/go.mod b/go.mod index 1cb5557..e8211fc 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 90d6818..a982f12 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/readme.md b/readme.md index 55e09bd..5315c53 100644 --- a/readme.md +++ b/readme.md @@ -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 \ No newline at end of file +- [x] Run bot on docker scratch: https://github.com/jeremyhuiskamp/golang-docker-scratch/blob/main/README.mdа \ No newline at end of file