combine_hashtags 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 41bba2718e81df228766739a03720dcbdf59f9c6af8d21f53a1f55d851f95723
4
+ data.tar.gz: 1174d7affe8bb12fec186e969917a6fc247014b2adec60d2f342f0b8847534ae
5
+ SHA512:
6
+ metadata.gz: 3864435fbe84c63470ec42d66eeb0abb441fbaa40473afd47794fe89a53692e319e96b06a598a704580916412c1960e0dc541175b1873641f74118dc95db7fa3
7
+ data.tar.gz: 33053edc1f4effcb42f3d03315b9aaa21e5e21939d7b7302e52d272284a92f8806297204cdf083c3b785af9829c3f37744aaca2c9770d856c9d67391b1385a78
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ # Rubymine project files
2
+ .idea/
3
+ .idea/*
4
+
5
+ # credentials
6
+ .env*
7
+
8
+ pkg/*
9
+ *.gem
10
+ .bundle
11
+
12
+ # local data
13
+ storage/*
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rspec_status ADDED
@@ -0,0 +1,4 @@
1
+ example_id | status | run_time |
2
+ ------------------------------------ | ------ | --------------- |
3
+ ./spec/combine_hashtags_spec.rb[1:1] | passed | 0.0006 seconds |
4
+ ./spec/combine_hashtags_spec.rb[1:2] | passed | 0.00028 seconds |
data/.rubocop.yml ADDED
@@ -0,0 +1,16 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
14
+
15
+ Style/FrozenStringLiteralComment:
16
+ Exclude: 'lib/combine_hashtags/view.rb'
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in combine_hashtags.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
11
+
12
+ gem "rubocop", "~> 1.21"
13
+
14
+ gem "dotenv"
15
+ gem "json"
16
+ gem "rest-client"
data/Gemfile.lock ADDED
@@ -0,0 +1,80 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ combine_hashtags (0.1.0)
5
+ dotenv
6
+ json
7
+ rest-client
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ ast (2.4.2)
13
+ diff-lcs (1.5.0)
14
+ domain_name (0.5.20190701)
15
+ unf (>= 0.0.5, < 1.0.0)
16
+ dotenv (2.7.6)
17
+ http-accept (1.7.0)
18
+ http-cookie (1.0.4)
19
+ domain_name (~> 0.5)
20
+ json (2.6.1)
21
+ mime-types (3.4.1)
22
+ mime-types-data (~> 3.2015)
23
+ mime-types-data (3.2022.0105)
24
+ netrc (0.11.0)
25
+ parallel (1.21.0)
26
+ parser (3.1.0.0)
27
+ ast (~> 2.4.1)
28
+ rainbow (3.1.1)
29
+ rake (13.0.6)
30
+ regexp_parser (2.2.0)
31
+ rest-client (2.1.0)
32
+ http-accept (>= 1.7.0, < 2.0)
33
+ http-cookie (>= 1.0.2, < 2.0)
34
+ mime-types (>= 1.16, < 4.0)
35
+ netrc (~> 0.8)
36
+ rexml (3.2.5)
37
+ rspec (3.10.0)
38
+ rspec-core (~> 3.10.0)
39
+ rspec-expectations (~> 3.10.0)
40
+ rspec-mocks (~> 3.10.0)
41
+ rspec-core (3.10.1)
42
+ rspec-support (~> 3.10.0)
43
+ rspec-expectations (3.10.1)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.10.0)
46
+ rspec-mocks (3.10.2)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.10.0)
49
+ rspec-support (3.10.3)
50
+ rubocop (1.24.1)
51
+ parallel (~> 1.10)
52
+ parser (>= 3.0.0.0)
53
+ rainbow (>= 2.2.2, < 4.0)
54
+ regexp_parser (>= 1.8, < 3.0)
55
+ rexml
56
+ rubocop-ast (>= 1.15.1, < 2.0)
57
+ ruby-progressbar (~> 1.7)
58
+ unicode-display_width (>= 1.4.0, < 3.0)
59
+ rubocop-ast (1.15.1)
60
+ parser (>= 3.0.1.1)
61
+ ruby-progressbar (1.11.0)
62
+ unf (0.1.4)
63
+ unf_ext
64
+ unf_ext (0.0.8)
65
+ unicode-display_width (2.1.0)
66
+
67
+ PLATFORMS
68
+ x86_64-linux
69
+
70
+ DEPENDENCIES
71
+ combine_hashtags!
72
+ dotenv
73
+ json
74
+ rake (~> 13.0)
75
+ rest-client
76
+ rspec (~> 3.0)
77
+ rubocop (~> 1.21)
78
+
79
+ BUNDLED WITH
80
+ 2.3.5
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 sofia alvarez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # CombineHashtags
2
+
3
+ Welcome!
4
+ This CLI gem helps to search for Instagram posts by using a combinations of hashtags.
5
+ This first version is limited to Instagram Basic Display permissions so it will only search for posts from a single profile.
6
+ Using this gem requires having a Facebook Developper account in order to get an Instagram User Access Token.
7
+ Currently, only the CLI implementation is available, but I'm working on a UI implementation for Rails web apps.
8
+
9
+
10
+ ## Requirements
11
+
12
+ Your own credentials for Instagram's Basic Display API:
13
+ - A Facebook Developer Account.
14
+ - An Instagram account with media.
15
+ - A public website that you own. This can be a generic free one like a Github Page or Heroku web app, or your actual website.
16
+ - For more info: https://developers.facebook.com/docs/instagram-basic-display-api/getting-started "
17
+
18
+ - Instagram authorization tokens are short-lived by default (about 1 hour). It is *strongly recommended* to get a long-lived token to facilitate the use of this gem.
19
+ - A future version might include a script to help with the authorization process. In the meantime, see here for how to get started: https://developers.facebook.com/docs/instagram-basic-display-api/guides/getting-access-tokens-and-permissions
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ gem 'combine_hashtags'
27
+ ```
28
+
29
+ And then execute:
30
+
31
+ $ bundle install
32
+
33
+ Or install it yourself as:
34
+
35
+ $ gem install combine_hashtags
36
+
37
+ ## Usage
38
+
39
+ - Start by storing your API credentials: run `bin/set-env` and follow the instructions.
40
+ - Load your first profile: run `bin/first-query`
41
+ - All set! run `bin/console` to access the interactive menu
42
+
43
+
44
+ ## Development
45
+
46
+ After checking out the repo, run `bin/setup` to install dependencies.
47
+
48
+
49
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
50
+
51
+ ## Contributing
52
+
53
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sofofos/combine_hashtags.
54
+
55
+ ## License
56
+
57
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bin/console ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "combine_hashtags"
6
+ require "combine_hashtags/cli/cli"
7
+
8
+
9
+ CombineHashtags::CLI.new.start
data/bin/first-query ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # executable to query the api and get json data stored locally in rails
3
+
4
+ # add project lib folder to the load path
5
+ lib_dir = File.realpath('../lib', __dir__)
6
+ $LOAD_PATH.unshift lib_dir
7
+
8
+ require "dotenv/load"
9
+ require "combine_hashtags"
10
+ require "combine_hashtags/query"
11
+
12
+
13
+ @query = CombineHashtags::Query.new
14
+
15
+ @query.call_api
16
+
17
+ puts "files loaded at #{ENV["STORAGE_PATH"]}"
data/bin/set-env ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Store environment variables securely in a .env file
4
+
5
+ # save user input to .env file
6
+ def save_to_env(key, value)
7
+ output = File.open( ".env", "a" )
8
+ output << "#{key}=#{value} \n"
9
+ output.close
10
+ end
11
+
12
+ # Make sure output starts on a new line
13
+ save_to_env("\n", "")
14
+
15
+ # Stores instagram access token
16
+ puts "Type your Instagram API access token:"
17
+ puts " >"
18
+ access_token = gets.chomp
19
+
20
+ save_to_env("ACCESS_TOKEN", access_token)
21
+
22
+ # Stores path for storage directory (API response will be stored here)
23
+ puts "Set a path for local storage of the profile data"
24
+ puts " >"
25
+ storage_path = gets.chomp
26
+ storage_path += "/" unless storage_path[-1] == "/"
27
+
28
+ save_to_env("STORAGE_PATH", storage_path)
29
+
30
+ # Set a filename for saving API's fetched data locally
31
+ puts "Add a file name for your profile data (no extension)"
32
+ puts " >"
33
+ file_name = gets.chomp
34
+ file_name += "-00.json"
35
+
36
+ file_path = "#{storage_path}#{file_name}"
37
+ save_to_env("FILE_PATH", file_path)
38
+
39
+ puts ""
40
+ puts "All done"
data/bin/setup ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
9
+ echo "to set your API access token, run bin/set-keys"
10
+
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/combine_hashtags/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "combine_hashtags"
7
+ spec.version = CombineHashtags::VERSION
8
+ spec.authors = ["sofia alvarez"]
9
+ spec.email = ["sofofos.sz@gmail.com"]
10
+
11
+ spec.summary = "Search combinations of hashtags using the Instagram API"
12
+ spec.description = "First version limited to Instagram Basic Display permissions
13
+ As such, using this gem requires having a Facebook Developper account in order to get an Instagram User Access Token
14
+ For more info: https://developers.facebook.com/docs/instagram-basic-display-api/getting-started "
15
+
16
+ spec.homepage = "https://github.com/sofofos/combine-hashtags"
17
+ spec.license = "MIT"
18
+ spec.required_ruby_version = ">= 2.6.0"
19
+
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features)/})
23
+ end
24
+
25
+ spec.bindir = "bin"
26
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ # Uncomment to register a new dependency of your gem
30
+ # spec.add_dependency "example-gem", "~> 1.0"
31
+ spec.add_development_dependency "rspec", "~> 3.2"
32
+
33
+ # gems required
34
+ spec.add_dependency "dotenv"
35
+ spec.add_dependency "json"
36
+ spec.add_dependency "rest-client"
37
+
38
+ # For more information and examples about making a new gem, checkout our
39
+ # guide at: https://bundler.io/guides/creating_gem.html
40
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../lib/combine_hashtags/post"
4
+ require_relative "../lib/combine_hashtags/profile"
5
+ require_relative "../lib/combine_hashtags/query"
6
+ require_relative "../lib/combine_hashtags/controller"
7
+ require_relative "../lib/combine_hashtags/cli/router"
8
+ require_relative "../lib/combine_hashtags/cli/view"
9
+ require_relative "../lib/combine_hashtags/cli/cli"
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ require "dotenv/load"
3
+ require "combine_hashtags/profile"
4
+ require "combine_hashtags/controller"
5
+ require "combine_hashtags/cli/router"
6
+
7
+ # methods for CLI implementation
8
+ class CombineHashtags::CLI
9
+
10
+ def initialize
11
+ @file_path = ENV["FILE_PATH"]
12
+ @profile = CombineHashtags::Profile.new(@file_path)
13
+ fetch_all
14
+
15
+ @controller = CombineHashtags::Controller.new(@profile)
16
+ @router = CombineHashtags::Router.new(@controller)
17
+ end
18
+
19
+ # sets new file_path/name for results of query's "next" pagination, update the profile, rinse repeat
20
+ def fetch_all
21
+ 10.times do |i|
22
+ new_path = ENV["FILE_PATH"].gsub("00", "0#{i}")
23
+ @profile.update(new_path)
24
+ end
25
+ end
26
+
27
+ # start cli
28
+ def start
29
+ @router.run
30
+ end
31
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: tru
2
+
3
+ # Router for CLI
4
+ class CombineHashtags::Router
5
+ def initialize(controller)
6
+ @controller = controller
7
+ @running = true
8
+ end
9
+
10
+ def run
11
+ puts "Welcome!"
12
+ puts "---"
13
+
14
+ while @running
15
+ display_tasks
16
+ action = gets.chomp.to_i
17
+ print `clear`
18
+ route_action(action)
19
+ end
20
+ end
21
+
22
+ def route_action(action)
23
+ case action
24
+ when 1 then @controller.setup
25
+ when 2 then @controller.list
26
+ when 3 then @controller.search
27
+
28
+ when 0 then stop
29
+ else
30
+ puts "Please select an option"
31
+ end
32
+ end
33
+
34
+ def stop
35
+ @running = false
36
+ end
37
+
38
+ def display_tasks
39
+ puts ""
40
+ puts "What do you want to do next?"
41
+ puts "1 - Fetch data for a new profile"
42
+ puts "2 - See profile's most popular hashtags"
43
+ puts "3 - Start a search"
44
+ puts " ---"
45
+ puts "0 - Stop and exit the program"
46
+ end
47
+ end
@@ -0,0 +1,70 @@
1
+ # View class for CLI display
2
+ class CombineHashtags::View
3
+ def initialize
4
+ @tags = {}
5
+ @list = "These are your 10 most popular hashtags:"
6
+ @search = "Showing X results:"
7
+ end
8
+
9
+ # formatting title appearance in CLI
10
+ def title(type)
11
+ title = type == "list" ? @list : @search
12
+ puts " --------------------------------------------"
13
+ puts " #{title}"
14
+ puts " --------------------------------------------"
15
+ end
16
+
17
+ def tags(content)
18
+ title("list")
19
+ content.each_with_index do |value, index|
20
+ puts " #{index + 1} - #{value[0]} appears #{value[1]} times in this profile"
21
+ puts ""
22
+ end
23
+ end
24
+
25
+ def results(results)
26
+ @search.gsub!("X", results.size.to_s)
27
+ title("search")
28
+ puts ""
29
+ links(results)
30
+ puts ""
31
+ end
32
+
33
+ def links(results)
34
+ results.each_with_index do |value, index|
35
+ preview = value.caption.gsub("\n", "").scan(/^[a-zA-Z\s\S][^#@]*/)
36
+ puts ""
37
+ puts " #{index + 1} - Post preview:"
38
+ puts " #{preview.first}"
39
+ puts " Other hashtags:"
40
+
41
+ preview_tags(value.tags)
42
+
43
+ puts ""
44
+ puts " Go to => #{value.post_url}"
45
+ puts " "
46
+ puts "* * * * * * * * * * * * * * * * * * * * * *"
47
+ end
48
+ end
49
+
50
+ def preview_tags(tags)
51
+ # TODO: refactor... chaining methods mehhhhh?
52
+ group = tags.split(" ").sample(10).each_slice(5).to_a
53
+ group.each do |set|
54
+ puts " #{set.to_s}"
55
+ end
56
+ end
57
+
58
+ def search
59
+ puts "Type the 1st hashtag you would like to search with"
60
+ puts ">"
61
+ @tags[:first] = gets.chomp
62
+ # yield if block_given?
63
+ puts "Type the 2nd hashtag you would like to search with"
64
+ puts ">"
65
+ @tags[:second] = gets.chomp
66
+ puts "Enter a 3rd hashtag, or press enter to skip"
67
+ @tags[:third] = gets.chomp
68
+ @tags
69
+ end
70
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ # TODO: move @view actions to CLI to improve CLI/UI abstraction
3
+
4
+ # controller class to direct user actions
5
+ class CombineHashtags::Controller
6
+ def initialize(profile)
7
+ @profile = profile
8
+ @posts = clean_posts
9
+ @view = CombineHashtags::View.new
10
+ @query = CombineHashtags::Query.new
11
+ end
12
+
13
+ # fetches data for a new profile and stores it locally
14
+ def setup
15
+ # removing previous profile
16
+ @profile.destroy
17
+ puts "loading..."
18
+ @query.call_api
19
+ end
20
+
21
+ # removes posts w/o hashtags
22
+ def clean_posts
23
+ @profile.posts.select { |post| post unless post.tags.empty? }
24
+ end
25
+
26
+ # returns 10 most popular/used hashtags
27
+ def list
28
+ content = CombineHashtags::Post.popular.sort_by { |_, value| -value }
29
+ @view.tags(content.first(10))
30
+ end
31
+
32
+ # displays popular hashtags, gets search keywords from user, returns matching posts
33
+ def search
34
+ list
35
+ keywords = @view.search
36
+ results = match(keywords)
37
+ @view.results(results)
38
+ end
39
+
40
+ # checks if keywords match any of the tags included in each post
41
+ def match(keywords)
42
+ first_set = @posts.select { |post| post.tags.include?(keywords[:first]) }
43
+ second_set = first_set.select { |post| post.tags.include?(keywords[:second]) }
44
+ third_set = second_set.select { |post| post.tags.include?(keywords[:third]) }
45
+ keywords[:third].empty? ? second_set : third_set
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ # require_relative "../combine_hashtags"
5
+
6
+ # Post class for profile content
7
+ class CombineHashtags::Post
8
+ attr_accessor :tags, :caption, :post_url, :img_url, :date
9
+
10
+ @@tags = []
11
+
12
+ def initialize(post_url, img_url, caption, timestamp)
13
+ @post_url = post_url
14
+ @img_url = img_url
15
+ @caption = caption
16
+ @date = Date.parse(timestamp)
17
+ @tags = []
18
+ extract_tags
19
+ end
20
+
21
+ def extract_tags
22
+ @tags << @caption.scan(/#\w*/)
23
+ @tags.flatten!
24
+ @@tags << @tags
25
+ @tags = @tags.join(" ")
26
+ end
27
+
28
+ # TODO: verify if redundant & remove (added flatten! to instance var ^)
29
+ def self.tags
30
+ @@tags.flatten
31
+ end
32
+
33
+ def self.popular
34
+ tags.tally.select { |_, value| value > 3 }
35
+ end
36
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ # TODO: Change class name, or move methods out, as its maybe
3
+ # not so clear why Profile deals with file operations?
4
+
5
+ require "json"
6
+ require "combine_hashtags/post"
7
+
8
+ # Profile class to instantiate a user's profile
9
+ # and load their IG content from JSON stored locally
10
+ class CombineHashtags::Profile
11
+ attr_accessor :posts
12
+
13
+ # loads json files to create profile and posts
14
+ def initialize(file_path)
15
+ @posts = []
16
+ update(file_path)
17
+ end
18
+
19
+ def load_file
20
+ if File.file?(@file_path)
21
+ file = File.read(@file_path)
22
+ @json_data = JSON.parse(file)
23
+ else
24
+ puts "file #{@file_path} not found"
25
+ end
26
+ end
27
+
28
+ def load_posts
29
+ @json_data["data"].map do |post|
30
+ post_url = post["permalink"]
31
+ img_url = post["media_url"]
32
+ caption = post["caption"] || ""
33
+ timestamp = post["timestamp"]
34
+
35
+ @posts << CombineHashtags::Post.new(post_url, img_url, caption, timestamp)
36
+ end
37
+ end
38
+
39
+ # update profile with remaining json files
40
+ def update(file_path)
41
+ @file_path = file_path
42
+ load_file
43
+ load_posts
44
+ end
45
+
46
+ # remove existing profile data
47
+ def destroy
48
+ puts "removing current files"
49
+ string = "#{ENV["STORAGE_PATH"]}*.json"
50
+ `rm #{string}`
51
+ end
52
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rest-client"
4
+ require "dotenv/load"
5
+
6
+ # Queries the instagram API with basic permissions AKA only self"s profile
7
+ class CombineHashtags::Query
8
+ def initialize
9
+ @request = build_query
10
+ end
11
+
12
+ # put together the query URL with access token set in .env
13
+ def build_query
14
+ base = "https://graph.instagram.com/me/media"
15
+ access_token = ENV["ACCESS_TOKEN"]
16
+ field = "id,timestamp,caption,media_url,permalink,media_type"
17
+
18
+ "#{base}?fields=#{field}&access_token=#{access_token}"
19
+ end
20
+
21
+ def call_api
22
+ begin
23
+ response = RestClient.get @request
24
+ # prompt the user in case of missing or invalid access_token
25
+ rescue RestClient::BadRequest
26
+ bad_request_msg
27
+ else
28
+ parse(response)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # moved to its own methods to keep call_api light-ish
35
+ def bad_request_msg
36
+ puts "Something went wrong. Check that your access token is valid and correctly set in your project's .env"
37
+ puts "Press 0 to exit"
38
+ end
39
+
40
+ def parse(response)
41
+ posts_json = JSON.parse(response)
42
+ save(posts_json)
43
+
44
+ # parse next page of results until we reach the last page
45
+ get_next_query(posts_json) unless posts_json["paging"]["next"].nil?
46
+ end
47
+
48
+ def save(data_json)
49
+ time = Time.now
50
+
51
+ # Forces a value between 0-9 to name the files sequentially
52
+ file_id = time.sec % 10
53
+ new_file = ENV["FILE_PATH"].gsub("00","0#{file_id}")
54
+ File.write("#{new_file}", JSON.dump(data_json))
55
+ end
56
+
57
+ def get_next_query(data_json)
58
+ @request = data_json["paging"]["next"]
59
+ call_api
60
+ end
61
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CombineHashtags
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "combine_hashtags/version"
4
+ require_relative "../config/environment"
5
+
6
+ module CombineHashtags
7
+ class Error < StandardError; end
8
+ end
9
+
@@ -0,0 +1,4 @@
1
+ module CombineHashtags
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: combine_hashtags
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - sofia alvarez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.2'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rest-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: "First version limited to Instagram Basic Display permissions\n As
70
+ such, using this gem requires having a Facebook Developper account in order to get
71
+ an Instagram User Access Token\n For more info: https://developers.facebook.com/docs/instagram-basic-display-api/getting-started "
72
+ email:
73
+ - sofofos.sz@gmail.com
74
+ executables:
75
+ - console
76
+ - first-query
77
+ - set-env
78
+ - setup
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - ".gitignore"
83
+ - ".rspec"
84
+ - ".rspec_status"
85
+ - ".rubocop.yml"
86
+ - Gemfile
87
+ - Gemfile.lock
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - bin/console
92
+ - bin/first-query
93
+ - bin/set-env
94
+ - bin/setup
95
+ - combine_hashtags.gemspec
96
+ - config/environment.rb
97
+ - lib/combine_hashtags.rb
98
+ - lib/combine_hashtags/cli/cli.rb
99
+ - lib/combine_hashtags/cli/router.rb
100
+ - lib/combine_hashtags/cli/view.rb
101
+ - lib/combine_hashtags/controller.rb
102
+ - lib/combine_hashtags/post.rb
103
+ - lib/combine_hashtags/profile.rb
104
+ - lib/combine_hashtags/query.rb
105
+ - lib/combine_hashtags/version.rb
106
+ - sig/combine_hashtags.rbs
107
+ homepage: https://github.com/sofofos/combine-hashtags
108
+ licenses:
109
+ - MIT
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: 2.6.0
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubygems_version: 3.2.32
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Search combinations of hashtags using the Instagram API
130
+ test_files: []