index.pl 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. #!/usr/bin/perl
  2. # Export Yammer group content as a strfile(1) file
  3. # Copyright 2014, Lubomir Rintel <lkundrak@v3.sk>
  4. # You can redistribute it and/or modify it under the terms of the
  5. # GNU Affero General Public License, version 3
  6. # <http://www.gnu.org/licenses/agpl-3.0.html>
  7. use strict;
  8. use warnings;
  9. our $client_id = 'FILLMEIN';
  10. our $client_secret = 'FILLMEIN';
  11. use CGI;
  12. require LWP::Protocol::https;
  13. use LWP::UserAgent;
  14. use JSON;
  15. use URI;
  16. use URI::Escape;
  17. require bytes;
  18. binmode *STDOUT, ':utf8';
  19. local our $q = new CGI;
  20. local our $code = $q->param ('code');
  21. local our $token = $q->param ('token');
  22. local our $group_id = $q->param ('group_id');
  23. local our $root = new URI ('https://www.yammer.com/');
  24. local our $ua = new LWP::UserAgent;
  25. $ua->default_header (Accept => 'application/json');
  26. sub req
  27. {
  28. my $uri = new URI (shift)->abs ($root);
  29. $uri->query_form (@_);
  30. # Try to rate limit message fetches
  31. if ($uri =~ /messages/) {
  32. our $last_time ||= 0;
  33. my $time = time - $last_time;
  34. sleep 3 - $time if $time < 3;
  35. $last_time = time;
  36. }
  37. my $res = $ua->get ($uri);
  38. # Rate limiting kicked in
  39. # It should not -- the above should make sure it won't
  40. if ($res->code == 429) {
  41. sleep 3;
  42. # Retry
  43. $res = $ua->request ($res->request);
  44. }
  45. die $res->status_line unless $res->is_success;
  46. return decode_json $res->decoded_content;
  47. }
  48. # Redirect to OAuth authenticator
  49. sub authenticate
  50. {
  51. my $auth_uri = "https://www.yammer.com/dialog/oauth?client_id=$client_id";
  52. # These need to be injected artifically, as mod_perl's CGI module
  53. # would generate a redundant payload for us. Shame.
  54. print "Location: $auth_uri\r\n";
  55. print "Status: 302 Please Authenticate\r\n";
  56. print "Content-Type: text/html; charset=>utf-8\r\n\r\n";
  57. print $q->start_html ('Log in to Yammer');
  58. print $q->h1 ('Please log in to fff Yammer');
  59. print $q->a ({ href => $auth_uri }, 'Log in');
  60. print $q->end_html;
  61. exit;
  62. }
  63. # List known groups with export links
  64. sub groups
  65. {
  66. print $q->header (-type => 'text/html', -charset => 'utf-8');
  67. print $q->start_html ('Pick a group');
  68. print $q->h1 ('Please pick a Group');
  69. print $q->start_ul;
  70. foreach my $group (@{req ('/api/v1/users/current.json',
  71. include_group_memberships => 'true')->{group_memberships}})
  72. {
  73. my $uri = new URI;
  74. $uri->query_form (token => $token, group_id => $group->{id});
  75. print $q->li ($q->a ({ href => $uri}, $group->{name}),
  76. $group->{description});
  77. }
  78. print $q->end_ul;
  79. print $q->end_html;
  80. exit;
  81. }
  82. # Obtain the authenticaion token
  83. if (not $token) {
  84. authenticate unless $code;
  85. $token = eval { req ('/oauth2/access_token.json',
  86. client_id => $client_id, client_secret => $client_secret,
  87. code => $code)->{access_token}{token} };
  88. authenticate unless $token;
  89. }
  90. $ua->default_header (Authorization => "Bearer $token");
  91. # Get a group number
  92. groups unless $group_id;
  93. # The export itself
  94. print $q->header (-type => 'text/plain', -charset => 'utf-8',
  95. '-transfer-encoding' => 'chunked');
  96. my $oldest_id;
  97. do {
  98. my @extra;
  99. @extra = (older_than => $oldest_id) if $oldest_id;
  100. my $uri = sprintf '/api/v1/messages/in_group/%s.json',
  101. uri_escape ($group_id);
  102. my @messages = @{req ($uri, @extra)->{messages}};
  103. $oldest_id = @messages ? $messages[$#messages]->{id} : undef;
  104. foreach my $msg (grep { $_->{message_type} eq 'update'
  105. and not $_->{replied_to_id} } @messages)
  106. {
  107. my $text = "%\n$msg->{body}{plain}\n";
  108. printf "%x\r\n%s\r\n", bytes::length ($text), $text;
  109. }
  110. flush STDOUT;
  111. } while ($oldest_id);
  112. # Chunking trailer
  113. print "0\r\n\r\n";
  114. exit;