notion_orbit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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