Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 0098ccb

Browse files
authored
feat: Implement upload photo to story functionality (#223)
* feat: Add media type constants * feat: Add UploadMedia client method * feat: Implement UploadMedia client method * feat: Implement UploadMedia service method * feat: Implement upload command * chore: Fix linters * chore: Fix linters * feat: Implement log error closure helper func * refactor: Use utils.LogError helper * fix: utils.LogError usage * refactor: Add proper errors handling * docs: Add comments * refactor: Use mediaTypeFlag * fix: Story photo flag usage * chore: Bump mongo version to 6.0.3 * feat: Implement add border to images * refactor: Move read file to handler layer * refactor: Early return for heif * refactor: Use if instead switch
1 parent aae67ff commit 0098ccb

61 files changed

Lines changed: 7563 additions & 13 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/instadiff-cli/commands.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,12 @@ func commands(ctx context.Context) []*cli.Command {
7676
Usage: "List diff account history (lost and new followers and followings)",
7777
Action: executeCmd(ctx, cmdListHistoryDiff),
7878
},
79+
{
80+
Name: "upload",
81+
Aliases: []string{"u"},
82+
Usage: "Upload media to profile",
83+
Action: executeCmd(ctx, cmdUploadMedia),
84+
Flags: uploadMediaFlags(),
85+
},
7986
}
8087
}

cmd/instadiff-cli/flags.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,20 @@ func addUsersFlag() *cli.StringSliceFlag {
5050
Value: &cli.StringSlice{},
5151
}
5252
}
53+
54+
func uploadMediaFlags() []cli.Flag {
55+
return []cli.Flag{
56+
&cli.StringFlag{
57+
Name: filePath,
58+
Usage: "Path to the media file",
59+
Required: true,
60+
Value: "",
61+
},
62+
&cli.BoolFlag{
63+
Name: mediaTypeStoryPhoto.String(),
64+
Usage: "If true - media will be uploaded as story photo",
65+
Required: false,
66+
Value: false,
67+
},
68+
}
69+
}

cmd/instadiff-cli/handlers.go

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"io"
78
"os"
9+
"path"
810
"sort"
911
"text/tabwriter"
1012
"time"
1113

14+
"github.com/obalunenko/instadiff-cli/internal/utils"
15+
1216
"github.com/urfave/cli/v2"
1317

1418
log "github.com/obalunenko/logger"
1519

20+
"github.com/obalunenko/instadiff-cli/internal/media"
1621
"github.com/obalunenko/instadiff-cli/internal/models"
1722
"github.com/obalunenko/instadiff-cli/internal/service"
1823
)
@@ -51,9 +56,7 @@ func executeCmd(ctx context.Context, f cmdFunc) cli.ActionFunc {
5156
}
5257

5358
defer func() {
54-
if err = svc.Stop(ctx); err != nil {
55-
log.WithError(ctx, err).Warn("Error occurred during the service stop")
56-
}
59+
utils.LogError(ctx, svc.Stop(ctx), "Error occurred during the service stop")
5760
}()
5861

5962
return f(c, svc)
@@ -387,3 +390,61 @@ func cmdListUseless(c *cli.Context, svc *service.Service) error {
387390

388391
return printUsersList(c, bots)
389392
}
393+
394+
var errEmptyFilePath = errors.New("path is empty")
395+
396+
func cmdUploadMedia(c *cli.Context, svc *service.Service) error {
397+
ctx := c.Context
398+
399+
p := c.String(filePath)
400+
401+
file, err := getMediaFile(ctx, p)
402+
if err != nil {
403+
return fmt.Errorf("get media file: %w", err)
404+
}
405+
406+
if err = svc.UploadMedia(ctx, file, getMediaType(c)); err != nil {
407+
return fmt.Errorf("upload media: %w", err)
408+
}
409+
410+
return nil
411+
}
412+
413+
func getMediaFile(ctx context.Context, fpath string) (io.Reader, error) {
414+
if fpath == "" {
415+
return nil, errEmptyFilePath
416+
}
417+
418+
f, err := os.Open(path.Clean(fpath))
419+
if err != nil {
420+
return nil, fmt.Errorf("open file: %w", err)
421+
}
422+
423+
defer func() {
424+
utils.LogError(ctx, f.Close(), "Failed to close file descriptor")
425+
}()
426+
427+
return f, nil
428+
}
429+
430+
//go:generate stringer -type=mediaTypeFlag -trimprefix=mediaTypeFlag -linecomment
431+
432+
type mediaTypeFlag uint
433+
434+
const (
435+
mediaTypeUndefined mediaTypeFlag = iota // undefined
436+
437+
mediaTypeStoryPhoto // story_photo
438+
439+
mediaTypeSentinel // sentinel
440+
)
441+
442+
func getMediaType(c *cli.Context) media.Type {
443+
mt := media.TypeUndefined
444+
445+
if c.Bool(mediaTypeStoryPhoto.String()) {
446+
mt = media.TypeStoryPhoto
447+
}
448+
449+
return mt
450+
}

cmd/instadiff-cli/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const (
2525
incognito = "incognito"
2626
users = "users"
2727
username = "username"
28+
filePath = "file_path"
2829
)
2930

3031
func main() {

cmd/instadiff-cli/mediatypeflag_string.go

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
version: "3.8"
1+
version: "3"
22
services:
33
mongo:
4-
image: "mongo:5.0.9"
4+
image: "mongo:6.0.3"
55
container_name: "mongo"
66
restart: unless-stopped
77
networks:

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.19
55
require (
66
github.com/Davincible/goinsta/v3 v3.1.3
77
github.com/briandowns/spinner v1.20.0
8+
github.com/disintegration/imaging v1.6.2
89
github.com/hashicorp/go-multierror v1.1.1
910
github.com/obalunenko/logger v0.5.1
1011
github.com/obalunenko/version v1.1.0
@@ -81,6 +82,7 @@ require (
8182
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
8283
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
8384
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
85+
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
8486
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b // indirect
8587
golang.org/x/sync v0.1.0 // indirect
8688
golang.org/x/sys v0.3.0 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG
8181
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8282
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
8383
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
84+
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
85+
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
8486
github.com/docker/cli v20.10.14+incompatible h1:dSBKJOVesDgHo7rbxlYjYsXe7gPzrTT+/cKQgpDAazg=
8587
github.com/docker/cli v20.10.14+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
8688
github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
@@ -363,6 +365,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
363365
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
364366
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
365367
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
368+
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
369+
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
366370
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
367371
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
368372
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

internal/client/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package client
33

44
import (
55
"context"
6+
"io"
67
"time"
78

89
"github.com/obalunenko/instadiff-cli/internal/client/instagram"
10+
"github.com/obalunenko/instadiff-cli/internal/media"
911
"github.com/obalunenko/instadiff-cli/internal/models"
1012
)
1113

@@ -22,6 +24,7 @@ type Client interface {
2224
Block(ctx context.Context, user models.User) error
2325
Unblock(ctx context.Context, user models.User) error
2426
IsUseless(ctx context.Context, user models.User, threshold int) (bool, error)
27+
UploadMedia(ctx context.Context, file io.Reader, mt media.Type) error
2528
Logout(ctx context.Context) error
2629
}
2730

internal/client/instagram/instagram.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"errors"
77
"fmt"
8+
"io"
89
"os"
910
"path/filepath"
1011
"strings"
@@ -20,6 +21,7 @@ import (
2021

2122
"github.com/obalunenko/instadiff-cli/pkg/spinner"
2223

24+
"github.com/obalunenko/instadiff-cli/internal/media"
2325
"github.com/obalunenko/instadiff-cli/internal/models"
2426
)
2527

@@ -242,6 +244,42 @@ func challenge(cl *goinsta.Instagram, chURL string) (*goinsta.Instagram, error)
242244
return cl, nil
243245
}
244246

247+
// UploadMedia uploads media to the profile.
248+
func (c *Client) UploadMedia(ctx context.Context, file io.Reader, mt media.Type) error {
249+
itm, err := c.client.Upload(&goinsta.UploadOptions{
250+
File: file,
251+
Thumbnail: nil,
252+
Album: nil,
253+
Caption: "",
254+
IsStory: isStory(mt),
255+
IsIGTV: false,
256+
Title: "",
257+
IGTVPreview: false,
258+
MuteAudio: false,
259+
DisableComments: false,
260+
DisableLikeViewCount: false,
261+
DisableSubtitles: false,
262+
UserTags: nil,
263+
AlbumTags: nil,
264+
Location: nil,
265+
})
266+
if err != nil {
267+
return err
268+
}
269+
270+
log.WithFields(ctx, log.Fields{
271+
"id": itm.ID,
272+
"media_type_inst": itm.MediaType,
273+
"is_story": isStory,
274+
}).Info("Uploaded")
275+
276+
return nil
277+
}
278+
279+
func isStory(mt media.Type) bool {
280+
return mt == media.TypeStoryPhoto
281+
}
282+
245283
// IsUseless reports where user is useless for statistics.
246284
func (c *Client) IsUseless(ctx context.Context, user models.User, threshold int) (bool, error) {
247285
u, err := c.client.Profiles.ByName(user.UserName)

0 commit comments

Comments
 (0)