Artifact f10669f8a12e371823cd82a5731228d0fe2657baa55db16b9f0564ce3d37f83f:
- File
stackexchange-favs-to-pinboard.rb
— part of check-in
[09c6947036]
at
2017-03-12 14:54:59
on branch master
— Make this actually work for lots of favs
Don't know if somehting changed or this was just always semi-broken
(probable), but noticed today it wasn't adding any new favs when I knew
for a fact I had some; Perhaps, also, I have a lot more favs than when I
first wrote this.Previously this assumed that the response returned all favs for a site
whereas actually by default it returns just 30. Perhaps by luck I'd been
picking up new ones because of using "sort=activity"; a strange choice
on it's own as if I'd used "sort=added" and run this fairly regularly
perhaps I'd never had noticed any issue? Anyway, now it does the correct
thing which is pull 100 at a time and look for the has_more parameter in
what is returned so it knows whether it needs to fetch another page.This does mean get_favs now returns an array of items (makes more sense)
than the actual encompassing response as it used to. (user: base@atomicules.co.uk size: 4010)
0000: 23 53 74 61 63 6b 45 78 63 68 61 6e 67 65 20 46 #StackExchange F
0010: 61 76 73 20 74 6f 20 50 69 6e 62 6f 61 72 64 0a avs to Pinboard.
0020: 72 65 71 75 69 72 65 20 27 6f 70 65 6e 2d 75 72 require 'open-ur
0030: 69 27 0a 72 65 71 75 69 72 65 20 27 6a 73 6f 6e i'.require 'json
0040: 27 0a 72 65 71 75 69 72 65 20 27 6f 70 74 70 61 '.require 'optpa
0050: 72 73 65 27 0a 72 65 71 75 69 72 65 20 27 63 67 rse'.require 'cg
0060: 69 27 0a 72 65 71 75 69 72 65 20 27 6c 6f 67 67 i'.require 'logg
0070: 65 72 27 0a 0a 0a 6f 70 74 70 61 72 73 65 20 3d er'...optparse =
0080: 20 4f 70 74 69 6f 6e 50 61 72 73 65 72 2e 6e 65 OptionParser.ne
0090: 77 20 64 6f 20 7c 6f 70 74 73 7c 0a 09 6f 70 74 w do |opts|..opt
00a0: 73 2e 6f 6e 28 27 2d 69 27 2c 20 27 2d 2d 69 64 s.on('-i', '--id
00b0: 20 49 44 27 2c 20 22 53 74 61 63 6b 65 78 63 68 ID', "Stackexch
00c0: 61 6e 67 65 20 49 44 22 29 20 7b 20 7c 69 7c 20 ange ID") { |i|
00d0: 53 74 61 63 6b 49 44 20 3d 20 69 20 7d 0a 09 6f StackID = i }..o
00e0: 70 74 73 2e 6f 6e 28 27 2d 75 27 2c 20 27 2d 2d pts.on('-u', '--
00f0: 75 73 65 72 20 55 53 45 52 27 2c 20 22 50 69 6e user USER', "Pin
0100: 62 6f 61 72 64 20 75 73 65 72 6e 61 6d 65 22 29 board username")
0110: 20 7b 20 7c 75 7c 20 55 73 65 72 20 3d 20 75 20 { |u| User = u
0120: 7d 0a 09 6f 70 74 73 2e 6f 6e 28 27 2d 74 27 2c }..opts.on('-t',
0130: 20 27 2d 2d 74 6f 6b 65 6e 20 54 4f 4b 45 4e 27 '--token TOKEN'
0140: 2c 20 22 50 69 6e 62 6f 61 72 64 20 41 50 49 20 , "Pinboard API
0150: 54 6f 6b 65 6e 22 29 20 7b 20 7c 74 7c 20 54 6f Token") { |t| To
0160: 6b 65 6e 20 3d 20 74 20 7d 0a 65 6e 64 0a 6f 70 ken = t }.end.op
0170: 74 70 61 72 73 65 2e 70 61 72 73 65 21 0a 0a 0a tparse.parse!...
0180: 64 65 66 20 67 65 74 5f 73 69 74 65 73 28 69 64 def get_sites(id
0190: 29 0a 09 72 65 73 70 6f 6e 73 65 20 3d 20 6f 70 )..response = op
01a0: 65 6e 28 22 68 74 74 70 73 3a 2f 2f 61 70 69 2e en("https://api.
01b0: 73 74 61 63 6b 65 78 63 68 61 6e 67 65 2e 63 6f stackexchange.co
01c0: 6d 2f 32 2e 32 2f 75 73 65 72 73 2f 23 7b 69 64 m/2.2/users/#{id
01d0: 7d 2f 61 73 73 6f 63 69 61 74 65 64 22 29 0a 09 }/associated")..
01e0: 70 61 72 73 65 64 20 3d 20 70 61 72 73 65 28 72 parsed = parse(r
01f0: 65 73 70 6f 6e 73 65 2e 72 65 61 64 29 0a 65 6e esponse.read).en
0200: 64 0a 0a 0a 64 65 66 20 70 61 72 73 65 28 72 65 d...def parse(re
0210: 73 70 6f 6e 73 65 5f 73 74 72 69 6e 67 29 0a 09 sponse_string)..
0220: 23 53 65 65 6d 73 20 6c 69 6b 65 20 52 75 62 79 #Seems like Ruby
0230: 20 61 75 74 6f 6d 61 74 69 63 61 6c 6c 79 20 68 automatically h
0240: 61 6e 64 6c 65 73 20 67 7a 69 70 20 63 6f 6d 70 andles gzip comp
0250: 72 65 73 73 69 6f 6e 20 6e 6f 77 2e 20 4e 6f 20 ression now. No
0260: 6e 65 65 64 20 74 6f 20 64 65 63 6f 6d 70 72 65 need to decompre
0270: 73 73 2e 0a 09 70 61 72 73 65 64 20 3d 20 4a 53 ss...parsed = JS
0280: 4f 4e 2e 70 61 72 73 65 28 72 65 73 70 6f 6e 73 ON.parse(respons
0290: 65 5f 73 74 72 69 6e 67 29 0a 65 6e 64 0a 0a 0a e_string).end...
02a0: 64 65 66 20 67 65 74 5f 66 61 76 73 28 73 69 74 def get_favs(sit
02b0: 65 2c 20 69 64 29 0a 09 70 75 74 73 20 22 46 65 e, id)..puts "Fe
02c0: 74 63 68 69 6e 67 20 66 61 76 73 20 66 6f 72 20 tching favs for
02d0: 23 7b 73 69 74 65 7d 22 0a 09 68 61 73 5f 6d 6f #{site}"..has_mo
02e0: 72 65 20 3d 20 74 72 75 65 0a 09 69 74 65 6d 73 re = true..items
02f0: 20 3d 20 5b 5d 0a 09 70 61 67 65 20 3d 20 31 0a = []..page = 1.
0300: 09 77 68 69 6c 65 20 68 61 73 5f 6d 6f 72 65 0a .while has_more.
0310: 09 09 72 65 73 70 6f 6e 73 65 20 3d 20 6f 70 65 ..response = ope
0320: 6e 28 22 68 74 74 70 73 3a 2f 2f 61 70 69 2e 73 n("https://api.s
0330: 74 61 63 6b 65 78 63 68 61 6e 67 65 2e 63 6f 6d tackexchange.com
0340: 2f 32 2e 32 2f 75 73 65 72 73 2f 23 7b 69 64 7d /2.2/users/#{id}
0350: 2f 66 61 76 6f 72 69 74 65 73 3f 6f 72 64 65 72 /favorites?order
0360: 3d 64 65 73 63 26 73 6f 72 74 3d 61 64 64 65 64 =desc&sort=added
0370: 26 70 61 67 65 53 69 7a 65 3d 31 30 30 26 70 61 &pageSize=100&pa
0380: 67 65 3d 23 7b 70 61 67 65 7d 26 73 69 74 65 3d ge=#{page}&site=
0390: 23 7b 73 69 74 65 7d 22 29 0a 09 09 23 55 73 65 #{site}")...#Use
03a0: 20 72 65 73 70 6f 6e 73 65 2e 72 65 61 64 20 6e response.read n
03b0: 6f 74 20 2e 73 74 72 69 6e 67 20 61 73 20 73 6f ot .string as so
03c0: 6d 65 20 72 65 74 75 72 6e 73 20 61 72 65 20 6c me returns are l
03d0: 61 72 67 65 20 65 6e 6f 75 67 68 20 74 6f 20 67 arge enough to g
03e0: 6f 20 74 6f 20 61 20 74 65 6d 70 20 66 69 6c 65 o to a temp file
03f0: 0a 09 09 70 61 72 73 65 64 20 3d 20 70 61 72 73 ...parsed = pars
0400: 65 28 72 65 73 70 6f 6e 73 65 2e 72 65 61 64 29 e(response.read)
0410: 0a 09 09 69 74 65 6d 73 20 2b 3d 20 70 61 72 73 ...items += pars
0420: 65 64 5b 22 69 74 65 6d 73 22 5d 0a 09 09 68 61 ed["items"]...ha
0430: 73 5f 6d 6f 72 65 20 3d 20 70 61 72 73 65 64 5b s_more = parsed[
0440: 22 68 61 73 5f 6d 6f 72 65 22 5d 0a 09 09 70 61 "has_more"]...pa
0450: 67 65 20 2b 3d 20 31 0a 09 65 6e 64 0a 09 70 75 ge += 1..end..pu
0460: 74 73 20 22 23 7b 69 74 65 6d 73 2e 6c 65 6e 67 ts "#{items.leng
0470: 74 68 7d 20 66 61 76 73 22 0a 09 69 74 65 6d 73 th} favs"..items
0480: 0a 65 6e 64 0a 0a 0a 63 6c 61 73 73 20 50 69 6e .end...class Pin
0490: 62 6f 61 72 64 0a 09 40 40 6c 6f 67 67 65 72 20 board..@@logger
04a0: 3d 20 4c 6f 67 67 65 72 2e 6e 65 77 28 53 54 44 = Logger.new(STD
04b0: 4f 55 54 29 0a 09 40 40 6c 6f 67 67 65 72 2e 6c OUT)..@@logger.l
04c0: 65 76 65 6c 20 3d 20 4c 6f 67 67 65 72 3a 3a 49 evel = Logger::I
04d0: 4e 46 4f 0a 09 40 40 72 61 74 65 5f 6c 69 6d 69 NFO..@@rate_limi
04e0: 74 20 3d 20 33 0a 09 41 70 69 5f 75 72 6c 20 3d t = 3..Api_url =
04f0: 20 22 68 74 74 70 73 3a 2f 2f 61 70 69 2e 70 69 "https://api.pi
0500: 6e 62 6f 61 72 64 2e 69 6e 2f 76 31 2f 22 0a 0a nboard.in/v1/"..
0510: 0a 09 64 65 66 20 69 6e 69 74 69 61 6c 69 7a 65 ..def initialize
0520: 28 75 73 65 72 2c 20 74 6f 6b 65 6e 29 0a 09 09 (user, token)...
0530: 40 75 73 65 72 20 3d 20 75 73 65 72 0a 09 09 40 @user = user...@
0540: 74 6f 6b 65 6e 20 3d 20 74 6f 6b 65 6e 0a 09 65 token = token..e
0550: 6e 64 0a 0a 0a 09 64 65 66 20 61 64 64 28 75 72 nd....def add(ur
0560: 6c 2c 20 64 65 73 63 72 69 70 74 69 6f 6e 2c 20 l, description,
0570: 65 78 74 65 6e 64 65 64 3d 6e 69 6c 2c 20 74 61 extended=nil, ta
0580: 67 73 3d 6e 69 6c 2c 20 72 65 70 6c 61 63 65 3d gs=nil, replace=
0590: 22 6e 6f 22 29 0a 09 09 61 74 74 65 6d 70 74 73 "no")...attempts
05a0: 20 3d 20 31 0a 09 09 70 6f 73 74 65 64 20 3d 20 = 1...posted =
05b0: 66 61 6c 73 65 0a 09 09 23 41 74 20 6d 69 6e 69 false...#At mini
05c0: 6d 75 6d 20 6d 75 73 74 20 68 61 76 65 20 75 72 mum must have ur
05d0: 6c 20 61 6e 64 20 64 65 73 63 72 69 70 74 69 6f l and descriptio
05e0: 6e 0a 09 09 61 72 72 61 79 5f 70 61 72 61 6d 65 n...array_parame
05f0: 74 65 72 73 20 3d 20 22 26 75 72 6c 3d 23 7b 43 ters = "&url=#{C
0600: 47 49 2e 65 73 63 61 70 65 28 75 72 6c 29 7d 26 GI.escape(url)}&
0610: 64 65 73 63 72 69 70 74 69 6f 6e 3d 23 7b 43 47 description=#{CG
0620: 49 2e 65 73 63 61 70 65 28 64 65 73 63 72 69 70 I.escape(descrip
0630: 74 69 6f 6e 29 7d 22 0a 09 09 23 43 6f 75 6c 64 tion)}"...#Could
0640: 20 6c 6f 6f 70 20 74 68 72 6f 75 67 68 20 74 68 loop through th
0650: 65 20 62 65 6c 6f 77 0a 09 09 75 6e 6c 65 73 73 e below...unless
0660: 20 65 78 74 65 6e 64 65 64 2e 6e 69 6c 3f 0a 09 extended.nil?..
0670: 09 09 61 72 72 61 79 5f 70 61 72 61 6d 65 74 65 ..array_paramete
0680: 72 73 20 2b 3d 20 22 26 65 78 74 65 6e 64 65 64 rs += "&extended
0690: 3d 23 7b 43 47 49 2e 65 73 63 61 70 65 28 65 78 =#{CGI.escape(ex
06a0: 74 65 6e 64 65 64 29 7d 22 0a 09 09 65 6e 64 0a tended)}"...end.
06b0: 09 09 75 6e 6c 65 73 73 20 74 61 67 73 2e 6e 69 ..unless tags.ni
06c0: 6c 3f 0a 09 09 09 23 54 4f 44 4f 3a 20 4e 65 65 l?....#TODO: Nee
06d0: 64 20 74 6f 20 63 68 65 63 6b 20 77 68 65 74 68 d to check wheth
06e0: 65 72 20 74 61 67 73 5f 65 73 63 61 70 65 64 20 er tags_escaped
06f0: 77 69 6c 6c 20 77 6f 72 6b 0a 09 09 09 61 72 72 will work....arr
0700: 61 79 5f 70 61 72 61 6d 65 74 65 72 73 20 2b 3d ay_parameters +=
0710: 20 22 26 74 61 67 73 3d 23 7b 43 47 49 2e 65 73 "&tags=#{CGI.es
0720: 63 61 70 65 28 74 61 67 73 29 7d 22 0a 09 09 65 cape(tags)}"...e
0730: 6e 64 0a 09 09 75 6e 74 69 6c 20 28 40 40 72 61 nd...until (@@ra
0740: 74 65 5f 6c 69 6d 69 74 20 3e 20 36 30 29 20 7c te_limit > 60) |
0750: 20 28 61 74 74 65 6d 70 74 73 20 3e 20 33 29 20 (attempts > 3)
0760: 7c 20 70 6f 73 74 65 64 20 0a 09 09 09 72 65 73 | posted ....res
0770: 70 6f 6e 73 65 20 3d 20 6f 70 65 6e 28 22 23 7b ponse = open("#{
0780: 41 70 69 5f 75 72 6c 7d 70 6f 73 74 73 2f 61 64 Api_url}posts/ad
0790: 64 3f 61 75 74 68 5f 74 6f 6b 65 6e 3d 23 7b 40 d?auth_token=#{@
07a0: 75 73 65 72 7d 3a 23 7b 40 74 6f 6b 65 6e 7d 22 user}:#{@token}"
07b0: 2b 61 72 72 61 79 5f 70 61 72 61 6d 65 74 65 72 +array_parameter
07c0: 73 2b 22 26 72 65 70 6c 61 63 65 3d 23 7b 72 65 s+"&replace=#{re
07d0: 70 6c 61 63 65 7d 26 66 6f 72 6d 61 74 3d 6a 73 place}&format=js
07e0: 6f 6e 22 29 0a 09 09 09 40 40 6c 6f 67 67 65 72 on")....@@logger
07f0: 2e 64 65 62 75 67 28 72 65 73 70 6f 6e 73 65 2e .debug(response.
0800: 73 74 72 69 6e 67 29 0a 09 09 09 40 40 6c 6f 67 string)....@@log
0810: 67 65 72 2e 64 65 62 75 67 28 72 65 73 70 6f 6e ger.debug(respon
0820: 73 65 2e 73 74 61 74 75 73 29 0a 09 09 09 72 65 se.status)....re
0830: 73 70 6f 6e 73 65 5f 6a 73 6f 6e 20 3d 20 4a 53 sponse_json = JS
0840: 4f 4e 2e 70 61 72 73 65 28 72 65 73 70 6f 6e 73 ON.parse(respons
0850: 65 2e 73 74 72 69 6e 67 29 0a 09 09 09 69 66 20 e.string)....if
0860: 28 72 65 73 70 6f 6e 73 65 2e 73 74 61 74 75 73 (response.status
0870: 5b 30 5d 20 3d 3d 20 22 32 30 30 22 29 20 26 20 [0] == "200") &
0880: 28 72 65 73 70 6f 6e 73 65 5f 6a 73 6f 6e 5b 22 (response_json["
0890: 72 65 73 75 6c 74 5f 63 6f 64 65 22 5d 20 3d 3d result_code"] ==
08a0: 20 22 64 6f 6e 65 22 29 0a 09 09 09 09 40 40 6c "done").....@@l
08b0: 6f 67 67 65 72 2e 69 6e 66 6f 28 22 41 64 64 65 ogger.info("Adde
08c0: 64 20 23 7b 75 72 6c 7d 22 29 0a 09 09 09 09 70 d #{url}").....p
08d0: 6f 73 74 65 64 20 3d 20 74 72 75 65 0a 09 09 09 osted = true....
08e0: 65 6c 73 69 66 20 28 72 65 73 70 6f 6e 73 65 2e elsif (response.
08f0: 73 74 61 74 75 73 5b 30 5d 20 3d 3d 20 22 32 30 status[0] == "20
0900: 30 22 29 20 26 20 28 72 65 73 70 6f 6e 73 65 5f 0") & (response_
0910: 6a 73 6f 6e 5b 22 72 65 73 75 6c 74 5f 63 6f 64 json["result_cod
0920: 65 22 5d 20 3d 3d 20 22 69 74 65 6d 20 61 6c 72 e"] == "item alr
0930: 65 61 64 79 20 65 78 69 73 74 73 22 29 0a 09 09 eady exists")...
0940: 09 09 40 40 6c 6f 67 67 65 72 2e 69 6e 66 6f 28 ..@@logger.info(
0950: 22 53 6b 69 70 70 69 6e 67 20 23 7b 75 72 6c 7d "Skipping #{url}
0960: 2c 20 61 6c 72 65 61 64 79 20 65 78 69 73 74 73 , already exists
0970: 22 29 0a 09 09 09 09 70 6f 73 74 65 64 20 3d 20 ").....posted =
0980: 74 72 75 65 0a 09 09 09 65 6c 73 69 66 20 72 65 true....elsif re
0990: 73 70 6f 6e 73 65 2e 73 74 61 74 75 73 5b 30 5d sponse.status[0]
09a0: 20 3d 3d 20 22 34 32 39 22 0a 09 09 09 09 23 20 == "429".....#
09b0: 34 32 39 20 54 6f 6f 20 4d 61 6e 79 20 52 65 71 429 Too Many Req
09c0: 75 65 73 74 73 2c 20 69 6e 63 72 65 61 73 65 20 uests, increase
09d0: 72 61 74 65 20 6c 69 6d 69 74 0a 09 09 09 09 40 rate limit.....@
09e0: 40 72 61 74 65 5f 6c 69 6d 69 74 20 2a 3d 20 32 @rate_limit *= 2
09f0: 0a 09 09 09 09 40 40 6c 6f 67 67 65 72 2e 77 61 .....@@logger.wa
0a00: 72 6e 28 22 52 61 74 65 20 4c 69 6d 69 74 20 69 rn("Rate Limit i
0a10: 6e 63 72 65 61 73 65 64 20 74 6f 20 23 7b 24 72 ncreased to #{$r
0a20: 61 74 65 5f 6c 69 6d 69 74 7d 20 73 65 63 6f 6e ate_limit} secon
0a30: 64 73 22 29 0a 09 09 09 65 6e 64 0a 09 09 09 61 ds")....end....a
0a40: 74 74 65 6d 70 74 73 20 2b 3d 20 31 0a 09 09 09 ttempts += 1....
0a50: 23 52 61 74 65 20 6c 69 6d 69 74 20 61 73 20 70 #Rate limit as p
0a60: 65 72 20 50 69 6e 62 6f 61 72 64 20 41 50 49 20 er Pinboard API
0a70: 72 65 71 75 69 72 65 6d 65 6e 74 73 0a 09 09 09 requirements....
0a80: 73 6c 65 65 70 20 40 40 72 61 74 65 5f 6c 69 6d sleep @@rate_lim
0a90: 69 74 0a 09 09 65 6e 64 0a 09 09 69 66 20 40 40 it...end...if @@
0aa0: 72 61 74 65 5f 6c 69 6d 69 74 20 3e 20 36 30 0a rate_limit > 60.
0ab0: 09 09 09 40 40 6c 6f 67 67 65 72 2e 65 72 72 6f ...@@logger.erro
0ac0: 72 28 22 52 61 74 65 20 6c 69 6d 69 74 20 68 61 r("Rate limit ha
0ad0: 73 20 65 78 63 65 65 64 65 64 20 36 30 20 73 65 s exceeded 60 se
0ae0: 63 73 2c 20 6c 65 74 27 73 20 74 72 79 20 61 67 cs, let's try ag
0af0: 61 69 6e 20 61 6e 6f 74 68 65 72 20 74 69 6d 65 ain another time
0b00: 22 29 0a 09 09 65 6c 73 69 66 20 61 74 74 65 6d ")...elsif attem
0b10: 70 74 73 20 3e 20 33 0a 09 09 09 40 40 6c 6f 67 pts > 3....@@log
0b20: 67 65 72 2e 65 72 72 6f 72 28 22 46 61 69 6c 65 ger.error("Faile
0b30: 64 20 33 20 74 69 6d 65 73 20 74 6f 20 73 61 76 d 3 times to sav
0b40: 65 20 23 7b 75 72 6c 7d 2c 20 62 6f 6d 62 69 6e e #{url}, bombin
0b50: 67 20 6f 75 74 22 29 0a 09 09 65 6e 64 0a 09 09 g out")...end...
0b60: 70 6f 73 74 65 64 0a 09 65 6e 64 0a 65 6e 64 0a posted..end.end.
0b70: 0a 0a 69 66 20 64 65 66 69 6e 65 64 3f 28 53 74 ..if defined?(St
0b80: 61 63 6b 49 44 29 20 61 6e 64 20 64 65 66 69 6e ackID) and defin
0b90: 65 64 3f 28 55 73 65 72 29 20 61 6e 64 20 64 65 ed?(User) and de
0ba0: 66 69 6e 65 64 3f 28 54 6f 6b 65 6e 29 0a 09 63 fined?(Token)..c
0bb0: 61 63 68 65 5f 66 69 6c 65 20 3d 20 45 4e 56 5b ache_file = ENV[
0bc0: 22 48 4f 4d 45 22 5d 2b 22 2f 2e 73 74 61 63 6b "HOME"]+"/.stack
0bd0: 65 78 63 68 61 6e 67 65 5f 66 61 76 73 5f 74 6f exchange_favs_to
0be0: 5f 70 69 6e 62 6f 61 72 64 22 0a 09 70 62 20 3d _pinboard"..pb =
0bf0: 20 50 69 6e 62 6f 61 72 64 2e 6e 65 77 28 55 73 Pinboard.new(Us
0c00: 65 72 2c 20 54 6f 6b 65 6e 29 0a 09 69 66 20 46 er, Token)..if F
0c10: 69 6c 65 2e 65 78 69 73 74 73 3f 28 63 61 63 68 ile.exists?(cach
0c20: 65 5f 66 69 6c 65 29 0a 09 09 63 61 63 68 65 20 e_file)...cache
0c30: 3d 20 4a 53 4f 4e 2e 70 61 72 73 65 28 46 69 6c = JSON.parse(Fil
0c40: 65 2e 72 65 61 64 28 63 61 63 68 65 5f 66 69 6c e.read(cache_fil
0c50: 65 29 29 0a 09 65 6c 73 65 0a 09 09 63 61 63 68 e))..else...cach
0c60: 65 20 3d 20 7b 7d 0a 09 65 6e 64 0a 09 70 61 72 e = {}..end..par
0c70: 73 65 64 20 3d 20 67 65 74 5f 73 69 74 65 73 28 sed = get_sites(
0c80: 53 74 61 63 6b 49 44 29 0a 09 70 61 72 73 65 64 StackID)..parsed
0c90: 5b 22 69 74 65 6d 73 22 5d 2e 65 61 63 68 20 64 ["items"].each d
0ca0: 6f 20 7c 73 69 74 65 7c 0a 09 09 66 61 76 73 20 o |site|...favs
0cb0: 3d 20 67 65 74 5f 66 61 76 73 28 73 69 74 65 5b = get_favs(site[
0cc0: 22 73 69 74 65 5f 75 72 6c 22 5d 2e 73 75 62 28 "site_url"].sub(
0cd0: 22 68 74 74 70 3a 2f 2f 22 2c 20 22 22 29 2e 73 "http://", "").s
0ce0: 75 62 28 22 68 74 74 70 73 3a 2f 2f 22 2c 20 22 ub("https://", "
0cf0: 22 29 2e 73 75 62 28 22 2e 73 74 61 63 6b 65 78 ").sub(".stackex
0d00: 63 68 61 6e 67 65 22 2c 20 22 22 29 2e 73 75 62 change", "").sub
0d10: 28 22 2e 63 6f 6d 22 2c 20 22 22 29 2c 20 73 69 (".com", ""), si
0d20: 74 65 5b 22 75 73 65 72 5f 69 64 22 5d 29 0a 09 te["user_id"])..
0d30: 09 23 44 6f 6e 27 74 20 6d 61 6b 65 20 6d 6f 72 .#Don't make mor
0d40: 65 20 74 68 61 6e 20 33 30 20 72 65 71 75 65 73 e than 30 reques
0d50: 74 73 20 70 65 72 20 73 65 63 6f 6e 64 0a 09 09 ts per second...
0d60: 73 6c 65 65 70 20 28 31 2e 30 2f 33 30 29 0a 09 sleep (1.0/30)..
0d70: 09 66 61 76 73 2e 65 61 63 68 20 64 6f 20 7c 66 .favs.each do |f
0d80: 61 76 7c 0a 09 09 09 74 69 74 6c 65 20 3d 20 66 av|....title = f
0d90: 61 76 5b 22 74 69 74 6c 65 22 5d 0a 09 09 09 74 av["title"]....t
0da0: 61 67 73 20 3d 20 66 61 76 5b 22 74 61 67 73 22 ags = fav["tags"
0db0: 5d 0a 09 09 09 6c 69 6e 6b 20 3d 20 66 61 76 5b ]....link = fav[
0dc0: 22 6c 69 6e 6b 22 5d 0a 09 09 09 23 43 68 65 63 "link"]....#Chec
0dd0: 6b 20 63 61 63 68 65 20 74 6f 20 73 65 65 20 69 k cache to see i
0de0: 66 20 61 6c 72 65 61 64 79 20 61 64 64 65 64 20 f already added
0df0: 74 6f 20 70 69 6e 62 6f 61 72 64 0a 09 09 09 75 to pinboard....u
0e00: 6e 6c 65 73 73 20 63 61 63 68 65 2e 68 61 73 5f nless cache.has_
0e10: 6b 65 79 3f 28 6c 69 6e 6b 29 0a 09 09 09 09 23 key?(link).....#
0e20: 4e 65 65 64 20 74 6f 20 75 6e 65 73 63 61 70 65 Need to unescape
0e30: 20 73 6f 20 63 61 6e 20 72 65 2d 65 73 63 61 70 so can re-escap
0e40: 65 20 69 6e 20 50 69 6e 62 6f 61 72 64 20 63 6f e in Pinboard co
0e50: 64 65 0a 09 09 09 09 23 53 74 69 6c 6c 20 77 61 de.....#Still wa
0e60: 6e 74 20 6e 6f 20 64 65 66 61 75 6c 74 20 72 65 nt no default re
0e70: 70 6c 61 63 65 2c 20 6a 75 73 74 20 69 6e 20 63 place, just in c
0e80: 61 73 65 20 63 61 63 68 65 20 64 6f 65 73 6e 27 ase cache doesn'
0e90: 74 20 65 78 69 73 74 0a 09 09 09 09 70 62 2e 61 t exist.....pb.a
0ea0: 64 64 28 6c 69 6e 6b 2c 20 43 47 49 2e 75 6e 65 dd(link, CGI.une
0eb0: 73 63 61 70 65 5f 68 74 6d 6c 28 74 69 74 6c 65 scape_html(title
0ec0: 29 2c 20 6e 69 6c 2c 20 74 61 67 73 2e 6a 6f 69 ), nil, tags.joi
0ed0: 6e 28 22 2c 20 22 29 2b 22 2c 20 73 74 61 63 6b n(", ")+", stack
0ee0: 65 78 63 68 61 6e 67 65 66 61 76 73 22 2c 20 22 exchangefavs", "
0ef0: 6e 6f 22 29 0a 09 09 09 09 23 41 64 64 20 74 6f no").....#Add to
0f00: 20 63 61 63 68 65 2e 20 53 68 6f 75 6c 64 20 63 cache. Should c
0f10: 68 65 63 6b 20 66 6f 72 20 73 75 63 63 65 73 73 heck for success
0f20: 20 72 65 61 6c 6c 79 0a 09 09 09 09 63 61 63 68 really.....cach
0f30: 65 5b 6c 69 6e 6b 5d 20 3d 20 74 72 75 65 0a 09 e[link] = true..
0f40: 09 09 65 6e 64 0a 09 09 65 6e 64 0a 09 65 6e 64 ..end...end..end
0f50: 0a 09 23 57 72 69 74 65 20 6f 75 74 20 63 61 63 ..#Write out cac
0f60: 68 65 0a 09 46 69 6c 65 2e 6f 70 65 6e 28 63 61 he..File.open(ca
0f70: 63 68 65 5f 66 69 6c 65 2c 20 22 77 22 29 20 64 che_file, "w") d
0f80: 6f 20 7c 66 69 6c 65 7c 0a 09 09 66 69 6c 65 20 o |file|...file
0f90: 3c 3c 20 63 61 63 68 65 2e 74 6f 5f 6a 73 6f 6e << cache.to_json
0fa0: 0a 09 65 6e 64 0a 65 6e 64 0a ..end.end.