notion_orbit 0.0.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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.env.sample +5 -0
  3. data/.github/CODE_OF_CONDUCT.md +134 -0
  4. data/.github/CONTRIBUTING.md +69 -0
  5. data/.github/workflows/CI.yml +25 -0
  6. data/.gitignore +34 -0
  7. data/Gemfile +11 -0
  8. data/Gemfile.lock +115 -0
  9. data/LICENSE +21 -0
  10. data/README.md +108 -0
  11. data/bin/console +16 -0
  12. data/bin/notion_orbit +28 -0
  13. data/bin/setup +8 -0
  14. data/docs/database-id-location.png +0 -0
  15. data/docs/new-note-screenshot.png +0 -0
  16. data/docs/setup-table-for-orbit.png +0 -0
  17. data/docs/setup.md +17 -0
  18. data/docs/ways-to-use.png +0 -0
  19. data/lib/notion_orbit.rb +12 -0
  20. data/lib/notion_orbit/client.rb +55 -0
  21. data/lib/notion_orbit/interactions/note.rb +37 -0
  22. data/lib/notion_orbit/notion.rb +42 -0
  23. data/lib/notion_orbit/notion_objects/block.rb +60 -0
  24. data/lib/notion_orbit/notion_objects/block_types/bulleted_list_item.rb +15 -0
  25. data/lib/notion_orbit/notion_objects/block_types/heading_1.rb +11 -0
  26. data/lib/notion_orbit/notion_objects/block_types/heading_2.rb +11 -0
  27. data/lib/notion_orbit/notion_objects/block_types/heading_3.rb +11 -0
  28. data/lib/notion_orbit/notion_objects/block_types/numbered_list_item.rb +15 -0
  29. data/lib/notion_orbit/notion_objects/block_types/paragraph.rb +15 -0
  30. data/lib/notion_orbit/notion_objects/block_types/to_do.rb +11 -0
  31. data/lib/notion_orbit/notion_objects/block_types/unsupported.rb +11 -0
  32. data/lib/notion_orbit/notion_objects/blocks.rb +18 -0
  33. data/lib/notion_orbit/notion_objects/rich_text.rb +36 -0
  34. data/lib/notion_orbit/orbit.rb +16 -0
  35. data/lib/notion_orbit/services/notion.rb +57 -0
  36. data/lib/notion_orbit/services/orbit.rb +73 -0
  37. data/lib/notion_orbit/version.rb +5 -0
  38. data/notion_orbit.gemspec +43 -0
  39. data/scripts/check_notes.rb +17 -0
  40. metadata +201 -0
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "dotenv/load"
6
+ require "notion_orbit"
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require "irb"
16
+ IRB.start(__FILE__)
data/bin/notion_orbit ADDED
@@ -0,0 +1,28 @@
1
+
2
+ #!/usr/bin/env ruby
3
+ require 'optparse'
4
+
5
+ check_notes = false
6
+
7
+ options = {}
8
+ choices = OptionParser.new do |opts|
9
+ opts.banner = "Usage: notion_orbit --check-notes"
10
+ opts.on("-h", "--help", "Prints help instructions") do
11
+ puts opts
12
+ exit
13
+ end
14
+ opts.on("--check-notes", "Check for new notes") do
15
+ check_notes = true
16
+ end
17
+ end.parse!
18
+
19
+ $LOAD_PATH.unshift(File.expand_path('../lib/notion_orbit', __dir__))
20
+
21
+ require_relative '../lib/notion_orbit'
22
+ require_relative '../scripts/check_notes'
23
+
24
+ if check_notes
25
+ puts "Checking for new notes and posting them to your Orbit workspace..."
26
+ ARGV[0] = 'render'
27
+ NotionOrbit::Scripts::CheckNotes.start(ARGV)
28
+ end
data/bin/setup ADDED
@@ -0,0 +1,8 @@
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
Binary file
Binary file
Binary file
data/docs/setup.md ADDED
@@ -0,0 +1,17 @@
1
+ <small><a href="../README.md">Back to README</a></small>
2
+
3
+ # First Time Setup
4
+
5
+ During the setup, we will need to create a new Notion integration which will allow this library to interact with the platform. This process takes about 2 minutes to finish. The application will contain an API secret which we should take note of for later.
6
+
7
+ 1. Head to [Notion Integrations](https://www.notion.so/my-integrations) and login in to your account.
8
+ 2. Click on the "+ New Integration" button on the left-hand side of the page. *Note: You must have admin access to your Notion workspace.*
9
+ 3. Fill out the details in the request form and press the "Submit" button.
10
+ 4. Save the "Internal Integration Token" from Notion. You will use it in this application. Do not share it with others and keep it in a secure location.
11
+ 5. Make sure that the "Integration Type" is set to "Internal Integration".
12
+ 6. Click "Save changes".
13
+ 7. You must add your integration to the database in your Notion workspace that you wish to fetch notes from.
14
+ * From the top navigation bar in your Notion database view press the "Share" button
15
+ * Find your integration by the name you gave it in the request form
16
+ * Select the integration and click "Invite"<br/>
17
+ ![](https://files.readme.io/0a267dd-share-database-with-integration.gif)
Binary file
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+ require_relative "notion_orbit/version"
5
+ require "notion-ruby-client"
6
+
7
+ module NotionOrbit
8
+ loader = Zeitwerk::Loader.new
9
+ loader.tag = File.basename(__FILE__, ".rb")
10
+ loader.push_dir(__dir__)
11
+ loader.setup
12
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dotenv/load"
4
+ require "net/http"
5
+ require "json"
6
+
7
+ # Create a client to log Notion database activities in your Orbit workspace
8
+ # Credentials can either be passed in to the instance or be loaded
9
+ # from environment variables
10
+ #
11
+ # @example
12
+ # client = NotionOrbit::Client.new
13
+ #
14
+ # @option params [String] :orbit_api_key
15
+ # The API key for the Orbit API
16
+ #
17
+ # @option params [String] :orbit_workspace
18
+ # The workspace ID for the Orbit workspace
19
+ #
20
+ # @option params [String] :notion_api_key
21
+ # The API key for Notion
22
+ #
23
+ # @option params [String] :notion_workspace_slug
24
+ # The Notion workspace slug
25
+ #
26
+ # @option params [String] :notion_database_id
27
+ # The Notion database ID
28
+ #
29
+ # @param [Hash] params
30
+ #
31
+ # @return [NotionOrbit::Client]
32
+ #
33
+ module NotionOrbit
34
+ class Client
35
+ attr_accessor :orbit_api_key, :orbit_workspace, :notion_database_id, :notion_api_key, :notion_workspace_slug
36
+
37
+ def initialize(params = {})
38
+ @orbit_api_key = params.fetch(:orbit_api_key, ENV["ORBIT_API_KEY"])
39
+ @orbit_workspace = params.fetch(:orbit_workspace, ENV["ORBIT_WORKSPACE_ID"])
40
+ @notion_api_key = params.fetch(:notion_api_key, ENV["NOTION_API_KEY"])
41
+ @notion_database_id = params.fetch(:notion_database_id, ENV["NOTION_DATABASE_ID"])
42
+ @notion_workspace_slug = params.fetch(:notion_workspace_slug, ENV["NOTION_WORKSPACE_SLUG"])
43
+ end
44
+
45
+ def notes
46
+ NotionOrbit::Notion.new(
47
+ orbit_api_key: @orbit_api_key,
48
+ orbit_workspace: @orbit_workspace,
49
+ notion_api_key: @notion_api_key,
50
+ notion_database_id: @notion_database_id,
51
+ notion_workspace_slug: @notion_workspace_slug
52
+ ).process_notes
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module NotionOrbit
6
+ module Interactions
7
+ class Note
8
+ def initialize(note:, orbit_api_key:, orbit_workspace:, notion_api_key:)
9
+ @note = note
10
+ @orbit_workspace = orbit_workspace
11
+ @orbit_api_key = orbit_api_key
12
+ @notion_api_key = notion_api_key
13
+
14
+ after_initialize!
15
+ end
16
+
17
+ def after_initialize!
18
+ orbit_service = NotionOrbit::Services::Orbit.new(orbit_workspace: @orbit_workspace, orbit_api_key: @orbit_api_key)
19
+ notion_service = NotionOrbit::Services::Notion.new(token: @notion_api_key)
20
+
21
+ orbit_service.send_note(
22
+ member_slug: @note[:member_slug],
23
+ api_key: @orbit_api_key,
24
+ content: @note[:content]
25
+ )
26
+
27
+ notion_service.mark_note_as_synced(@note[:page_id], orbit_note_url(@note[:member_slug]))
28
+ end
29
+
30
+ private
31
+
32
+ def orbit_note_url(member_slug)
33
+ "https://app.orbit.love/#{@orbit_workspace}/members/#{member_slug}?type=notes"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionOrbit
4
+ class Notion
5
+ def initialize(params = {})
6
+ @orbit_api_key = params.fetch(:orbit_api_key)
7
+ @orbit_workspace = params.fetch(:orbit_workspace)
8
+ @notion_api_key = params.fetch(:notion_api_key)
9
+ @notion_database_id = params.fetch(:notion_database_id)
10
+ @notion_workspace_slug = params.fetch(:notion_workspace_slug)
11
+ end
12
+
13
+ def process_notes
14
+ notion_service = NotionOrbit::Services::Notion.new(token: @notion_api_key)
15
+ orbit_service = NotionOrbit::Services::Orbit.new(orbit_workspace: @orbit_workspace, orbit_api_key: @orbit_api_key)
16
+
17
+ notes = notion_service.notes(database_id: @notion_database_id)
18
+
19
+ notes.each do |note|
20
+ next if note[:properties][:email].nil? || note[:properties][:email] == ""
21
+
22
+ member_slug = orbit_service.member_slug(email: note[:properties][:email])
23
+ next if member_slug == "" || member_slug.nil?
24
+
25
+ NotionOrbit::Orbit.call(
26
+ type: "note",
27
+ data: {
28
+ note: {
29
+ email: note[:properties][:email],
30
+ member_slug: member_slug,
31
+ page_id: note[:properties][:page_id],
32
+ content: note[:content]
33
+ }
34
+ },
35
+ orbit_workspace: @orbit_workspace,
36
+ orbit_api_key: @orbit_api_key,
37
+ notion_api_key: @notion_api_key
38
+ )
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,60 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ class Block
4
+ class << self
5
+ def new_from_raw_block(raw_block, indentation: 0)
6
+ klass = case raw_block.type
7
+ when 'bulleted_list_item'
8
+ NotionOrbit::NotionObjects::BlockTypes::BulletedListItem
9
+ when 'heading_1'
10
+ NotionOrbit::NotionObjects::BlockTypes::Heading1
11
+ when 'heading_2'
12
+ NotionOrbit::NotionObjects::BlockTypes::Heading2
13
+ when 'heading_3'
14
+ NotionOrbit::NotionObjects::BlockTypes::Heading3
15
+ when 'numbered_list_item'
16
+ NotionOrbit::NotionObjects::BlockTypes::NumberedListItem
17
+ when 'paragraph'
18
+ NotionOrbit::NotionObjects::BlockTypes::Paragraph
19
+ when 'to_do'
20
+ NotionOrbit::NotionObjects::BlockTypes::ToDo
21
+ else
22
+ NotionOrbit::NotionObjects::BlockTypes::Unsupported
23
+ end
24
+ klass.new(raw_block, indentation)
25
+ end
26
+ end
27
+
28
+ def initialize(raw_block, indentation)
29
+ @raw_block = raw_block
30
+ @id = raw_block.id
31
+ @type = raw_block.type
32
+ @has_children = raw_block.has_children
33
+ @indentation = indentation
34
+ end
35
+
36
+ def to_markdown
37
+ markdown = @raw_block[@type].text.map do |rich_text|
38
+ NotionOrbit::NotionObjects::RichText.new(rich_text).to_markdown
39
+ end.join
40
+
41
+ if @has_children
42
+ raw_children = notion_service.client.block_children(id: @id).results
43
+ children_indentation = indent_children? ? @indentation + 2 : @indentation
44
+ children_blocks = Blocks.new(raw_children, indentation: children_indentation)
45
+ markdown += "\n\n" + children_blocks.to_markdown
46
+ end
47
+
48
+ markdown
49
+ end
50
+
51
+ def indent_children?
52
+ false
53
+ end
54
+
55
+ def notion_service
56
+ @notion_service ||= NotionOrbit::Services::Notion.new
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class BulletedListItem < Block
5
+ def to_markdown
6
+ '- ' + super
7
+ end
8
+
9
+ def indent_children?
10
+ true
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class Heading1 < Block
5
+ def to_markdown
6
+ "# " + super
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class Heading2 < Block
5
+ def to_markdown
6
+ "## " + super
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class Heading3 < Block
5
+ def to_markdown
6
+ "### " + super
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class NumberedListItem < Block
5
+ def to_markdown
6
+ '1. ' + super
7
+ end
8
+
9
+ def indent_children?
10
+ true
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class Paragraph < Block
5
+ def to_markdown
6
+ super + "\n"
7
+ end
8
+
9
+ def indent_children?
10
+ false
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class ToDo < Block
5
+ def to_markdown
6
+ "[#{@raw_block[@type].checked ? 'x' : ' '}] " + super + "\n"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ module BlockTypes
4
+ class Unsupported < Block
5
+ def to_markdown
6
+ "[Block #{@type} is not supported yet]"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ class Blocks
4
+ include Enumerable
5
+
6
+ attr_accessor :blocks
7
+
8
+ def initialize(raw_blocks, indentation: 0)
9
+ @blocks = raw_blocks.map{ |raw_block| Block.new_from_raw_block(raw_block, indentation: indentation) }
10
+ @indentation = indentation
11
+ end
12
+
13
+ def to_markdown
14
+ @blocks.map { |block| " " * @indentation + block.to_markdown }.join("\n").rstrip.gsub("\n","\\n")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ module NotionOrbit
2
+ module NotionObjects
3
+ class RichText
4
+ def initialize(raw_rich_text)
5
+ @raw_rich_text = raw_rich_text
6
+ @type = raw_rich_text.type
7
+ @annotations = raw_rich_text.annotations
8
+ @text = raw_rich_text.text
9
+ end
10
+
11
+ def to_markdown
12
+ return "" unless @type == 'text'
13
+ markdown = @text.content
14
+ markdown = apply_link(markdown) unless @text.link.nil?
15
+ markdown = apply_annotations(markdown)
16
+ markdown
17
+ end
18
+
19
+ def apply_link(content)
20
+ @text.link.nil? ? content : "[#{content}](#{@text.link.url})"
21
+ end
22
+
23
+ def apply_annotations(content)
24
+ annotation_symbols = []
25
+ annotation_symbols << '**' if @annotations.bold
26
+ annotation_symbols << '_' if @annotations.italic
27
+ annotation_symbols << '`' if @annotations.code
28
+ wrap_with(content, annotation_symbols)
29
+ end
30
+
31
+ def wrap_with(string, wrappers)
32
+ "#{wrappers.join}#{string}#{wrappers.reverse.join}"
33
+ end
34
+ end
35
+ end
36
+ end