osa 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4ad6f3864c9196aff9026ad350f71c7904c27817dbd0217bf6008885830ac15
4
- data.tar.gz: fe44a6e9d98590eb231c04c794327ca1132feaf9d13d556563fe900ec5bce190
3
+ metadata.gz: 22c6dd2dc64261089df031e75fee841e1416276244674dce617792aacbb12fca
4
+ data.tar.gz: f0442134bc13835edcbacea2aa594fdc95202e47beed2e71c29f5214bbae2335
5
5
  SHA512:
6
- metadata.gz: c9dfa0f4cf44acf3ca08654f670cdca47a7a9ae7c681dfea0b9b9ddb6acf064a9b88c98dd325e449e9efa23647fc663cb407a4ec19a1fdd4e86df1a556fe467e
7
- data.tar.gz: 1f32dbf2f0cbf9373788100ca8f99579be14732cf86c91ce45c443d9ae67476d762eae81b7b9d3b5243b97011dd066df713f72dd8002ab53d32d9e27f145999f
6
+ metadata.gz: ca001d8b232f9bfa8bcfde6e75be2a6e1a7b2d025254e22af2feec25625d979e3bd10a88325cee888442b5686b22c7f93fd1866225d7bb6ca38e27e9c62e05af
7
+ data.tar.gz: 7d96e933b2c4b09d335f825c933ba01197399aea947e7ca2fca3f66a851c7f469f046fd37abbb59548c54fbb37835eef3684540172cf01bcf30489f2106e6208
@@ -6,6 +6,7 @@ AllCops:
6
6
  DisabledByDefault: true
7
7
  Exclude:
8
8
  - '**/vendor/**/*'
9
+ - 'bin/**'
9
10
 
10
11
  Bundler/DuplicatedGem:
11
12
  Enabled: true
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
2
3
 
3
4
  gemspec
4
5
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- osa (0.1.0)
4
+ osa (0.1.1)
5
5
  activerecord (~> 6.0)
6
6
  faraday (~> 1.1)
7
7
  public_suffix (~> 4.0)
data/Rakefile CHANGED
@@ -1,10 +1,11 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
- t.libs << "test"
6
- t.libs << "lib"
7
- t.test_files = FileList["test/**/*_test.rb"]
6
+ t.libs << 'test'
7
+ t.libs << 'lib'
8
+ t.test_files = FileList['test/**/*_test.rb']
8
9
  end
9
10
 
10
- task :default => :test
11
+ task default: :test
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "osa"
2
+ require 'bundler/setup'
3
+ require 'osa'
5
4
 
6
5
  # You can add fixtures and/or initialization code here to make experimenting
7
6
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +9,5 @@ require "osa"
10
9
  # require "pry"
11
10
  # Pry.start
12
11
 
13
- require "irb"
12
+ require 'irb'
14
13
  IRB.start(__FILE__)
data/bin/osa ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'osa' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require 'pathname'
11
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ bundle_binstub = File.expand_path('../bundle', __FILE__)
15
+
16
+ if File.file?(bundle_binstub)
17
+ if /This file was generated by Bundler/.match?(File.read(bundle_binstub, 300))
18
+ load(bundle_binstub)
19
+ else
20
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
21
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
22
+ end
23
+ end
24
+
25
+ require 'rubygems'
26
+ require 'bundler/setup'
27
+
28
+ load Gem.bin_path('osa', 'osa')
data/exe/osa CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
  require 'osa/services/setup_service'
3
4
  require 'osa/services/auth_service'
4
5
 
@@ -8,7 +9,7 @@ case cmd
8
9
  when 'setup'
9
10
  OSA::SetupService.new.setup!
10
11
  when 'login'
11
- OSA::AuthService.login(Config.first || Config.new)
12
+ OSA::AuthService.login(OSA::Config.first || OSA::Config.new)
12
13
  when 'scan-junk'
13
14
  require 'osa/scripts/scan_junk_folder'
14
15
  when 'scan-report'
data/lib/osa.rb CHANGED
@@ -1 +1,2 @@
1
- require "osa/version"
1
+ # frozen_string_literal: true
2
+ require 'osa/version'
@@ -15,6 +15,11 @@ module OSA
15
15
  handle_response(response)
16
16
  end
17
17
 
18
+ def put(*args, **kwargs)
19
+ response = @connection.put(*args, **kwargs)
20
+ handle_response(response)
21
+ end
22
+
18
23
  def delete(*args, **kwargs)
19
24
  response = @connection.delete(*args, **kwargs)
20
25
  handle_response(response)
@@ -31,7 +36,7 @@ module OSA
31
36
  if response.status > 299
32
37
  raise StandardError, "Request failed with status code: #{response.status}, body: #{response.body}"
33
38
  end
34
- if response.headers['content-type'].include?('application/json')
39
+ if response.headers['content-type']&.include?('application/json')
35
40
  JSON.parse(response.body)
36
41
  else
37
42
  response.body
@@ -4,43 +4,49 @@ require 'json'
4
4
  require 'base64'
5
5
  require 'osa/util/paginated'
6
6
  require 'osa/clients/http_client'
7
+ require 'active_support/core_ext/numeric/bytes'
8
+ require 'active_support'
7
9
 
8
10
  module OSA
9
- class MSGraphClient < HttpClient
11
+ class MSGraphClient
12
+ URL = 'https://graph.microsoft.com'
13
+
10
14
  def initialize(token)
11
- connection = Faraday.new(
15
+ @authenticated = HttpClient.new(Faraday.new(
12
16
  url: 'https://graph.microsoft.com',
13
17
  headers: {
14
18
  'authorization' => "Bearer #{token}"
15
19
  }
16
- )
17
- super connection
20
+ ))
21
+
22
+ @unauthenticated = HttpClient.new(Faraday.new(
23
+ url: 'https://graph.microsoft.com'
24
+ ))
18
25
  end
19
26
 
20
27
  def rules
21
- get('/v1.0/me/mailFolders/inbox/messageRules')
28
+ @authenticated.get('/v1.0/me/mailFolders/inbox/messageRules')
22
29
  end
23
30
 
24
31
  def rule(id)
25
- get("/v1.0/me/mailFolders/inbox/messageRules/#{id}")
32
+ @authenticated.get("/v1.0/me/mailFolders/inbox/messageRules/#{id}")
26
33
  end
27
34
 
28
35
  def folders
29
- Paginated.new(get('/v1.0/me/mailFolders'), self)
36
+ Paginated.new(@authenticated.get('/v1.0/me/mailFolders'), self)
30
37
  end
31
38
 
32
39
  def mails(folder_id)
33
- Paginated.new(get("/v1.0/me/mailFolders/#{folder_id}/messages"), self)
40
+ Paginated.new(@authenticated.get("/v1.0/me/mailFolders/#{folder_id}/messages"), self)
34
41
  end
35
42
 
36
43
  def raw_mail(mail_id)
37
- get("/v1.0/me/messages/#{mail_id}/$value")
44
+ @authenticated.get("/v1.0/me/messages/#{mail_id}/$value")
38
45
  end
39
46
 
40
47
  def forward_mail_as_attachment(mail_id, to)
41
48
  raw_mail = self.raw_mail(mail_id)
42
49
  forward_message = create_forward_message(mail_id)
43
- add_email_attachment(forward_message['id'], raw_mail)
44
50
  update = {
45
51
  toRecipients: [
46
52
  {
@@ -51,36 +57,68 @@ module OSA
51
57
  ]
52
58
  }
53
59
  update_message(forward_message['id'], update)
60
+ add_email_attachment(forward_message['id'], 'email.eml', raw_mail)
54
61
  send_message(forward_message['id'])
55
62
  end
56
63
 
57
64
  def create_forward_message(mail_id)
58
- post("/v1.0/me/messages/#{mail_id}/createForward")
65
+ @authenticated.post("/v1.0/me/messages/#{mail_id}/createForward")
59
66
  end
60
67
 
61
- def add_email_attachment(mail_id, content)
62
- body = {
63
- "@odata.type": '#microsoft.graph.fileAttachment',
64
- "contentBytes": Base64.encode64(content),
65
- "name": 'email.eml'
66
- }
67
- post("/v1.0/me/messages/#{mail_id}/attachments", body.to_json, 'content-type': 'application/json')
68
+ def add_email_attachment(mail_id, name, content)
69
+ if content.length < 3.megabytes
70
+ add_small_email_attachment(mail_id, name, content)
71
+ else
72
+ add_large_email_attachment(mail_id, name, content)
73
+ end
68
74
  end
69
75
 
70
76
  def delete_mail(mail_id)
71
- delete("/v1.0/me/messages/#{mail_id}")
77
+ @authenticated.delete("/v1.0/me/messages/#{mail_id}")
72
78
  end
73
79
 
74
80
  def update_rule(id, update)
75
- patch("/v1.0/me/mailFolders/inbox/messageRules/#{id}", update.to_json, 'content-type' => 'application/json')
81
+ @authenticated.patch("/v1.0/me/mailFolders/inbox/messageRules/#{id}", update.to_json, 'content-type' => 'application/json')
76
82
  end
77
83
 
78
84
  def update_message(id, update)
79
- patch("/v1.0/me/messages/#{id}", update.to_json, 'content-type': 'application/json')
85
+ @authenticated.patch("/v1.0/me/messages/#{id}", update.to_json, 'content-type': 'application/json')
80
86
  end
81
87
 
82
88
  def send_message(id)
83
- post("/v1.0/me/messages/#{id}/send")
89
+ @authenticated.post("/v1.0/me/messages/#{id}/send")
84
90
  end
91
+
92
+ private
93
+ def add_small_email_attachment(mail_id, name, content)
94
+ body = {
95
+ "@odata.type": '#microsoft.graph.fileAttachment',
96
+ contentBytes: Base64.encode64(content),
97
+ name: name
98
+ }
99
+ @authenticated.post("/v1.0/me/messages/#{mail_id}/attachments", body.to_json, 'content-type': 'application/json')
100
+ end
101
+
102
+ def add_large_email_attachment(mail_id, name, content)
103
+ upload_session = create_upload_session(mail_id, name, content.length)
104
+ ranges = upload_session['nextExpectedRanges'].map do |range|
105
+ range.split('-').then { |start, finish| (start.to_i..finish&.to_i) }
106
+ end
107
+ ranges.each do |range|
108
+ current_content = content[range]
109
+ @unauthenticated.put(upload_session['uploadUrl'], current_content[range], 'Content-Range': "bytes #{range.begin}-#{(range.end || content.length) - 1}/#{content.length}")
110
+ end
111
+ end
112
+
113
+ def create_upload_session(mail_id, name, size)
114
+ body = {
115
+ AttachmentItem: {
116
+ attachmentType: :file,
117
+ name: name,
118
+ size: size
119
+ }
120
+ }
121
+ @authenticated.post("/v1.0/me/messages/#{mail_id}/attachments/createUploadSession", body.to_json, 'content-type': 'application/json')
122
+ end
85
123
  end
86
124
  end
@@ -17,7 +17,6 @@ loop do
17
17
  puts "deleting spam from #{email_address}"
18
18
  context.graph_client.delete_mail(mail['id'])
19
19
 
20
-
21
20
  next if email_address.nil?
22
21
  domain = PublicSuffix.domain(email_address.split('@', 2)[1])
23
22
 
@@ -39,7 +39,7 @@ module OSA
39
39
  base_uri = 'https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize'
40
40
 
41
41
  uri = "#{base_uri}#{query_str}"
42
- puts "Open the following url in your browser and enter the authentication code displayed."
42
+ puts 'Open the following url in your browser and enter the authentication code displayed.'
43
43
  puts uri
44
44
 
45
45
  prompt = TTY::Prompt.new
@@ -3,5 +3,5 @@
3
3
  module OSA
4
4
  CLIENT_ID = 'befa4a9e-5d16-4a48-9792-4bd1d125abe8'
5
5
  REDIRECT_URL = 'https://storage.googleapis.com/outlook-spam-automator/login.html'
6
- SCOPE = 'https://graph.microsoft.com/MailboxSettings.ReadWrite https://graph.microsoft.com/MailboxSettings.ReadWrite offline_access'
6
+ SCOPE = 'https://graph.microsoft.com/Mail.ReadWrite https://graph.microsoft.com/MailboxSettings.ReadWrite offline_access'
7
7
  end
@@ -10,7 +10,7 @@ module OSA
10
10
  attr_reader :auth_service
11
11
  attr_reader :config
12
12
 
13
- def initialize(config=nil)
13
+ def initialize(config = nil)
14
14
  @config = config || Config.first
15
15
 
16
16
  if @config.nil?
@@ -5,10 +5,9 @@ ActiveRecord::Base.establish_connection(
5
5
  adapter: 'sqlite3',
6
6
  database: ENV['DATABASE'] || "#{Dir.pwd}/osa.db"
7
7
  )
8
- root = "#{File.dirname(__FILE__ )}/../"
8
+ root = "#{File.dirname(__FILE__)}/../"
9
9
  ActiveRecord::MigrationContext.new("#{root}/migrations", ActiveRecord::SchemaMigration).migrate
10
10
 
11
-
12
11
  module OSA
13
12
  class Blacklist < ActiveRecord::Base
14
13
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module OSA
2
- VERSION = "0.1.0"
3
+ VERSION = '0.1.1'
3
4
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require_relative 'lib/osa/version'
2
3
 
3
4
  Gem::Specification.new do |spec|
@@ -9,11 +10,11 @@ Gem::Specification.new do |spec|
9
10
  spec.summary = 'Outlook Spam Automator'
10
11
  spec.description = 'Get rid of spam on your Outlook account'
11
12
  spec.license = 'MIT'
12
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
13
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
13
14
 
14
15
  # Specify which files should be added to the gem when it is released.
15
16
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
16
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
17
18
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
19
  end
19
20
  spec.bindir = 'exe'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: osa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Moray Baruh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-20 00:00:00.000000000 Z
11
+ date: 2020-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -98,6 +98,7 @@ files:
98
98
  - README.md
99
99
  - Rakefile
100
100
  - bin/console
101
+ - bin/osa
101
102
  - bin/setup
102
103
  - exe/osa
103
104
  - lib/osa.rb