Module: Terms
- Defined in:
- lib/Terms.rb
Overview
First-run consent gate for ZMediumToMarkdown.
Workflow (priority high → low):
1. CLI flag `--accept-terms` (or `--decline-terms`) consumed before
OptionParser, so it never sees them.
2. ENV var ZMTM_TOS_ACCEPTED=1 persistent CI escape.
3. Local marker file ~/.zmediumtomarkdown/.tos-vN written on first
interactive accept.
4. Otherwise: print the summary and prompt on a TTY; refuse to run on
a non-TTY (so a piped invocation can't accidentally bypass).
Bumping ‘VERSION` invalidates every existing marker file, forcing every user to re-read and re-accept the new Terms.
Constant Summary collapse
- VERSION =
'v1'.freeze
- ENV_OPT_IN =
'ZMTM_TOS_ACCEPTED'.freeze
- ACCEPT_FLAG =
'--accept-terms'.freeze
- DECLINE_FLAG =
'--decline-terms'.freeze
- TERMS_URL =
'https://github.com/ZhgChgLi/ZMediumToMarkdown/blob/main/TERMS.md'.freeze
- PRIVACY_URL =
'https://github.com/ZhgChgLi/ZMediumToMarkdown/blob/main/PRIVACY.md'.freeze
Class Method Summary collapse
- .acceptDir ⇒ Object
-
.acceptedBannerLine ⇒ Object
One-line reminder used by CLI banners, after the Terms have been accepted.
- .acceptPath(version = VERSION) ⇒ Object
- .alreadyAccepted? ⇒ Boolean
-
.consumeFlags!(argv, errput: $stderr) ⇒ Object
Strips –accept-terms / –decline-terms out of argv (in place) before OptionParser sees them.
-
.ensureAccepted!(errput: $stderr, input: $stdin) ⇒ Object
Block until the user has explicitly accepted these Terms.
- .printSummary(io) ⇒ Object
- .writeAcceptance! ⇒ Object
Class Method Details
.acceptDir ⇒ Object
28 29 30 |
# File 'lib/Terms.rb', line 28 def acceptDir File.join(Dir.home, '.zmediumtomarkdown') end |
.acceptedBannerLine ⇒ Object
One-line reminder used by CLI banners, after the Terms have been accepted. Kept short.
124 125 126 |
# File 'lib/Terms.rb', line 124 def acceptedBannerLine "ℹ By using this tool you agreed to the Terms (#{VERSION}) — #{TERMS_URL}" end |
.acceptPath(version = VERSION) ⇒ Object
32 33 34 |
# File 'lib/Terms.rb', line 32 def acceptPath(version = VERSION) File.join(acceptDir, ".tos-#{version}-accepted") end |
.alreadyAccepted? ⇒ Boolean
81 82 83 84 |
# File 'lib/Terms.rb', line 81 def alreadyAccepted? return true if ENV[ENV_OPT_IN].to_s == '1' File.exist?(acceptPath) end |
.consumeFlags!(argv, errput: $stderr) ⇒ Object
Strips –accept-terms / –decline-terms out of argv (in place) before OptionParser sees them. Side effects: writing the marker on accept, SystemExit on decline.
39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/Terms.rb', line 39 def consumeFlags!(argv, errput: $stderr) if argv.delete(DECLINE_FLAG) errput.puts 'Terms declined. Exiting.' raise SystemExit.new(2) end if argv.delete(ACCEPT_FLAG) writeAcceptance! errput.puts "Terms #{VERSION} accepted (see #{acceptPath})." end end |
.ensureAccepted!(errput: $stderr, input: $stdin) ⇒ Object
Block until the user has explicitly accepted these Terms. Called before any network operation (see CLI#main).
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/Terms.rb', line 53 def ensureAccepted!(errput: $stderr, input: $stdin) return if alreadyAccepted? printSummary(errput) unless input.respond_to?(:tty?) && input.tty? errput.puts <<~MSG.chomp [!] Cannot prompt for consent on a non-interactive stream. Run this command in a terminal once, or set #{ENV_OPT_IN}=1, or pass #{ACCEPT_FLAG} on the command line. MSG raise SystemExit.new(2) end errput.print "Type 'yes' to accept and continue: " answer = input.gets answer = answer.to_s.strip.downcase if answer == 'yes' || answer == 'y' writeAcceptance! errput.puts "Accepted. (Marker written to #{acceptPath})" else errput.puts 'Declined. Exiting.' raise SystemExit.new(2) end end |
.printSummary(io) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/Terms.rb', line 95 def printSummary(io) io.puts <<~MSG ──────────────────────────────────────────────────────────────────── ZMediumToMarkdown — first-run notice (Terms #{VERSION}) ──────────────────────────────────────────────────────────────────── Medium's Terms of Service forbid automated access to its Services, including via browser plugins, scripts, and CLI tools like this one. Use of this tool may conflict with Medium's ToS. You are using it AT YOUR OWN RISK. The author (ZhgChgLi) accepts no liability for your use, including any account suspension, IP block, or legal claim by Medium or by the original article authors. By continuing you agree to: • only convert articles you have legitimate access to read, • respect the original author's copyright when redistributing, • not use this tool for mass scraping or commercial redistribution. Read the full Terms : #{TERMS_URL} Read the Privacy : #{PRIVACY_URL} (To bypass this prompt non-interactively, set #{ENV_OPT_IN}=1 or pass #{ACCEPT_FLAG} once.) ──────────────────────────────────────────────────────────────────── MSG end |
.writeAcceptance! ⇒ Object
86 87 88 89 90 91 92 93 |
# File 'lib/Terms.rb', line 86 def writeAcceptance! FileUtils.mkdir_p(acceptDir) File.write(acceptPath, "accepted_at: #{Time.now.utc.iso8601}\nversion: #{VERSION}\n") rescue StandardError => e # Don't crash if the home directory is read-only — env var still works. warn "[Terms] Could not write acceptance marker (#{e.class}: #{e.}). " \ "Re-prompt will appear next run unless #{ENV_OPT_IN}=1." end |