├── lib ├── api-utils.d.ts ├── api-structs.js ├── api-parameters.js ├── base36.d.ts ├── limit-to.d.ts ├── base36.js ├── limit-to.js ├── urlwatch.js ├── urlwatch.d.ts ├── api-structs.d.ts ├── index.d.ts ├── api-utils.js ├── api-parameters.d.ts └── index.js ├── src ├── base36.ts ├── limit-to.ts ├── urlwatch.ts ├── api-structs.ts ├── api-utils.ts ├── index.ts └── api-parameters.ts ├── README.md ├── LICENSE ├── package.json ├── .gitignore ├── examples ├── get-token.js └── gettoken.js └── tsconfig.json /lib/api-utils.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /lib/api-structs.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /lib/api-parameters.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Generate using plurk2, data from https://www.plurk.com/API/2/list 3 | // Do not edit manually! 4 | Object.defineProperty(exports, "__esModule", { value: true }); 5 | -------------------------------------------------------------------------------- /lib/base36.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `base36` is an utility that converts base36 string to or from number. 3 | */ 4 | export declare namespace base36 { 5 | /** 6 | * Base36 string to number. 7 | * @param val Base36 string. 8 | * @return {number} 9 | */ 10 | function decode(val: string): number; 11 | /** 12 | * Number to base36 string. 13 | * @param val Number. 14 | * @return {string} 15 | */ 16 | function encode(val: number): string; 17 | } 18 | -------------------------------------------------------------------------------- /src/base36.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `base36` is an utility that converts base36 string to or from number. 3 | */ 4 | export namespace base36 { 5 | /** 6 | * Base36 string to number. 7 | * @param val Base36 string. 8 | * @return {number} 9 | */ 10 | export function decode(val: string): number { 11 | return parseInt(val, 36); 12 | } 13 | 14 | /** 15 | * Number to base36 string. 16 | * @param val Number. 17 | * @return {string} 18 | */ 19 | export function encode(val: number): string { 20 | return isNaN(val) ? '' : val.toString(36); 21 | } 22 | } -------------------------------------------------------------------------------- /lib/limit-to.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `limitTo` is an utility namespace that encodes and decodes Plurk limitTo 3 | * field format to and from array which looks like this: `|1||2|`. 4 | */ 5 | export declare namespace limitTo { 6 | function parse(src: number[]): number[]; 7 | /** 8 | * Parses the limitTo format to array 9 | * @param src Source string. 10 | * @return {number[] | undefined} 11 | */ 12 | function parse(src: string): number[] | undefined; 13 | /** 14 | * Converts array of Plurk IDs to limitTo format. 15 | * @param src Source array of Plurk IDs. 16 | * @return {string} 17 | */ 18 | function stringify(src: number[]): string; 19 | } 20 | -------------------------------------------------------------------------------- /lib/base36.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.base36 = void 0; 4 | /** 5 | * `base36` is an utility that converts base36 string to or from number. 6 | */ 7 | var base36; 8 | (function (base36) { 9 | /** 10 | * Base36 string to number. 11 | * @param val Base36 string. 12 | * @return {number} 13 | */ 14 | function decode(val) { 15 | return parseInt(val, 36); 16 | } 17 | base36.decode = decode; 18 | /** 19 | * Number to base36 string. 20 | * @param val Number. 21 | * @return {string} 22 | */ 23 | function encode(val) { 24 | return isNaN(val) ? '' : val.toString(36); 25 | } 26 | base36.encode = encode; 27 | })(base36 = exports.base36 || (exports.base36 = {})); 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Node-Plurk2 2 | =========== 3 | Plurk2 is a full featured Plurk API 2.0 interface for Node.js. It wraps all requests with OAuth 1.0 headers which has been documented. Also it handles events from comet channel. 4 | 5 | Installation 6 | ---------- 7 | ```sh 8 | $ npm i --save plurk2 9 | ``` 10 | 11 | Usage 12 | ----- 13 | Documentation will be available [here](https://code.moka-rin.moe/node-plurk2/). 14 | 15 | ### Example Usage 16 | ```javascript 17 | const { PlurkClient } = require('plurk2'); 18 | 19 | const client = new PlurkClient('CONSUMER_TOKEN', 'CONSUMER_TOKEN_SECRET', 'ACCESS_TOKEN', 'ACCESS_TOKEN_SECRET'); 20 | 21 | client.request('Users/me') 22 | .then(profile => console.log(profile)) 23 | .catch(err => console.error(err)); 24 | ``` 25 | 26 | For more examples can have a look in [`examples/get-token.js`](examples/get-token.js). 27 | 28 | Reference: [Plurk API 2.0](https://www.plurk.com/API) 29 | 30 | License 31 | ------- 32 | [MIT](LICENSE) -------------------------------------------------------------------------------- /lib/limit-to.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.limitTo = void 0; 4 | /** 5 | * `limitTo` is an utility namespace that encodes and decodes Plurk limitTo 6 | * field format to and from array which looks like this: `|1||2|`. 7 | */ 8 | var limitTo; 9 | (function (limitTo) { 10 | var numberMatcher = /[0-9]+/g; 11 | function parse(src) { 12 | if (Array.isArray(src)) 13 | return src; 14 | if (typeof src === 'string') { 15 | var matches = src.match(numberMatcher); 16 | if (matches) 17 | return matches.map(function (id) { return parseInt(id, 10); }); 18 | } 19 | } 20 | limitTo.parse = parse; 21 | /** 22 | * Converts array of Plurk IDs to limitTo format. 23 | * @param src Source array of Plurk IDs. 24 | * @return {string} 25 | */ 26 | function stringify(src) { 27 | return src.length ? "|" + src.join('||') + "|" : ''; 28 | } 29 | limitTo.stringify = stringify; 30 | })(limitTo = exports.limitTo || (exports.limitTo = {})); 31 | -------------------------------------------------------------------------------- /lib/urlwatch.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.urlmatch = void 0; 4 | var base36_1 = require("./base36"); 5 | /** 6 | * `urlmatch` is an utility that extracts an user or a plurk's id from URL. 7 | */ 8 | var urlmatch; 9 | (function (urlmatch) { 10 | var plurkUrlMatcher = /plurk\.com\/(m\/)?p\/([0-9a-z]+)(\/#)?$/; 11 | var plurkUserMatcher = /plurk\.com\/(m\/u\/)?([0-9a-zA-Z_]+)(\/#)?$/; 12 | function plurk(url, decode) { 13 | var result = plurkUrlMatcher.exec(url); 14 | if (result) { 15 | var id = result[2]; 16 | if (id) 17 | return decode ? base36_1.base36.decode(id) : id; 18 | } 19 | } 20 | urlmatch.plurk = plurk; 21 | function user(url, decode) { 22 | var result = plurkUserMatcher.exec(url); 23 | if (result) { 24 | var id = result[2]; 25 | if (id) 26 | return decode ? base36_1.base36.decode(id) : id; 27 | } 28 | } 29 | urlmatch.user = user; 30 | })(urlmatch = exports.urlmatch || (exports.urlmatch = {})); 31 | -------------------------------------------------------------------------------- /src/limit-to.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `limitTo` is an utility namespace that encodes and decodes Plurk limitTo 3 | * field format to and from array which looks like this: `|1||2|`. 4 | */ 5 | export namespace limitTo { 6 | const numberMatcher: RegExp = /[0-9]+/g; 7 | 8 | export function parse(src: number[]): number[]; 9 | /** 10 | * Parses the limitTo format to array 11 | * @param src Source string. 12 | * @return {number[] | undefined} 13 | */ 14 | export function parse(src: string): number[] | undefined; 15 | export function parse(src: number[] | string): number[] | undefined { 16 | if(Array.isArray(src)) return src; 17 | if(typeof src === 'string') { 18 | const matches = src.match(numberMatcher); 19 | if(matches) return matches.map( 20 | (id: string): number => parseInt(id, 10)); 21 | } 22 | } 23 | 24 | /** 25 | * Converts array of Plurk IDs to limitTo format. 26 | * @param src Source array of Plurk IDs. 27 | * @return {string} 28 | */ 29 | export function stringify(src: number[]): string { 30 | return src.length ? `|${src.join('||')}|` : ''; 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Jeremy Lam (JLChnToZ). 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "plurk2", 3 | "version": "0.5.2", 4 | "description": "Full featured Plurk API 2.0 for Node.js", 5 | "main": "lib/index", 6 | "type": "lib/index", 7 | "scripts": { 8 | "build": "tsc", 9 | "generate-api-params": "tsc && node lib/api-utils && tsc", 10 | "build-doc": "typedoc --excludeNotExported --excludePrivate --theme minimal --mode file --target es5 --readme none --out ./docs/ ./", 11 | "prepublish": "npm run build" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/JLChnToZ/node-plurk2.git" 16 | }, 17 | "keywords": [ 18 | "plurk" 19 | ], 20 | "author": "Jeremy Lam \"JLChnToZ\"", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/JLChnToZ/node-plurk2/issues" 24 | }, 25 | "homepage": "https://github.com/JLChnToZ/node-plurk2#readme", 26 | "devDependencies": { 27 | "typedoc": "^0.14.2", 28 | "typescript": "^3.9.5" 29 | }, 30 | "dependencies": { 31 | "@types/node": "^10.17.26", 32 | "@types/request-promise": "^4.1.37", 33 | "request": "^2.88.0", 34 | "request-promise": "^4.2.4" 35 | }, 36 | "babel": { 37 | "presets": [ 38 | "env" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (http://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # Typescript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | 65 | # End of https://www.gitignore.io/api/node 66 | -------------------------------------------------------------------------------- /lib/urlwatch.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * `urlmatch` is an utility that extracts an user or a plurk's id from URL. 3 | */ 4 | export declare namespace urlmatch { 5 | /** 6 | * Extracts plurk id from URL provided. 7 | * @param url Url to parse. 8 | * @param decode Should automatic converts base36 id to number? 9 | * @return {string | undefined} 10 | */ 11 | function plurk(url: string, decode: false): string | undefined; 12 | /** 13 | * Extracts plurk id from URL provided. 14 | * @param url Url to parse. 15 | * @param decode Should automatic converts base36 id to number? 16 | * @return {number | undefined} 17 | */ 18 | function plurk(url: string, decode: true): number | undefined; 19 | /** 20 | * Extracts user id from URL provided. 21 | * @param url Url to parse. 22 | * @param decode Should automatic converts base36 id to number? 23 | * @return {string | undefined} 24 | */ 25 | function user(url: string, decode: false): string | undefined; 26 | /** 27 | * Extracts user id from URL provided. 28 | * @param url Url to parse. 29 | * @param decode Should automatic converts base36 id to number? 30 | * @return {number | undefined} 31 | */ 32 | function user(url: string, decode: true): number | undefined; 33 | } 34 | -------------------------------------------------------------------------------- /examples/get-token.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Readline = require('readline'); 3 | const { PlurkClient } = require('../lib/'); 4 | 5 | const client = new PlurkClient('CONSUMER_TOKEN', 'CONSUMER_TOKEN_SECRET'); 6 | 7 | Promise.resolve(client) 8 | .then(client => client.getRequestToken()) 9 | .then(({ authPage }) => { 10 | console.log('Go to this page to verify:', authPage); 11 | const rl = Readline.createInterface({ 12 | input: process.stdin, 13 | output: process.stdout 14 | }); 15 | return Promise.all([ 16 | rl, readlineQuestionPromise(rl, 'Enter or paste verifier here: ') 17 | ]); 18 | }) 19 | .then(([rl, verifier]) => { 20 | rl.close(); 21 | return client.getAccessToken(verifier); 22 | }) 23 | .then(client => { 24 | console.log('Login success\ntoken:', client.token, '\nsecret:', client.tokenSecret); 25 | // Get current user's profile 26 | return client.request('Users/me'); 27 | }) 28 | .then(profile => { 29 | console.log('My profile:', profile); 30 | // Get plurks just before 10 minutes from now 31 | return client.request('Polling/getPlurks', { 32 | offset: new Date(Date.now() - 10 * 60 * 1000) 33 | }); 34 | }) 35 | .then(info => { 36 | console.log('Read plurks before 10 minutes, count:', info.plurks.length); 37 | // Listen to comet channel 38 | client.startComet(); 39 | client.on('comet', () => console.log('New comet data arrived...')); 40 | client.on('new_plurk', response => console.log('[New Plurk]', response)); 41 | client.on('new_response', response => console.log('[New Response]', response)); 42 | client.on('error', err => console.error('[Error]', err.stack || err)); 43 | }) 44 | .catch(err => console.error('Error:', err.stack || err, err.response.request)); 45 | 46 | function readlineQuestionPromise(readline, question) { 47 | return new Promise(resolve => readline.question(question, resolve)); 48 | } -------------------------------------------------------------------------------- /examples/gettoken.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Readline = require('readline'); 3 | const util = require('util'); 4 | const yaml = require('node-yaml'); 5 | const { PlurkClient } = require('../lib/'); 6 | 7 | const client = new PlurkClient('Fr8niDHLBn66', 'spl0BbAZ0DnNaeb98wjOVn2woAdOgZ0m'); 8 | 9 | Promise.resolve(client) 10 | .then(client => client.getRequestToken()) 11 | .then(({ authPage }) => { 12 | console.log('Go to this page to verify:', authPage); 13 | const rl = Readline.createInterface({ 14 | input: process.stdin, 15 | output: process.stdout 16 | }); 17 | return Promise.all([ 18 | rl, readlineQuestionPromise(rl, 'Enter or paste verifier here: ') 19 | ]); 20 | }) 21 | .then(([rl, verifier]) => { 22 | rl.close(); 23 | return client.getAccessToken(verifier); 24 | }) 25 | .then(client => { 26 | console.log('Login success\ntoken:', client.token, '\nsecret:', client.tokenSecret); 27 | // Get current user's profile 28 | return client.request('Users/me'); 29 | }) 30 | .then(profile => { 31 | console.log('My profile:', yaml.dump(profile)); 32 | // Get plurks just before 10 minutes from now 33 | return client.request('Polling/getPlurks', { 34 | offset: new Date(Date.now() - 10 * 60 * 1000) 35 | }); 36 | }) 37 | .then(info => { 38 | console.log('Read plurks before 10 minutes...\n', yaml.dump(info.plurks)); 39 | // Listen to comet channel 40 | client.startComet(); 41 | client.on('comet', () => console.log('New comet data arrived...')); 42 | client.on('new_plurk', response => console.log('[New Plurk]', yaml.dump(response))); 43 | client.on('new_response', response => console.log('[New Response]', yaml.dump(response))); 44 | client.on('error', err => console.error('[Error]', err.stack || util.inspect(err, { colors: true }))); 45 | }) 46 | .catch(err => console.error('Error:', err.stack || util.inspect(err, { colors: true }))); 47 | 48 | function readlineQuestionPromise(readline, question) { 49 | return new Promise(resolve => readline.question(question, resolve)); 50 | } -------------------------------------------------------------------------------- /src/urlwatch.ts: -------------------------------------------------------------------------------- 1 | import { base36 } from "./base36"; 2 | 3 | /** 4 | * `urlmatch` is an utility that extracts an user or a plurk's id from URL. 5 | */ 6 | export namespace urlmatch { 7 | const plurkUrlMatcher: RegExp = /plurk\.com\/(m\/)?p\/([0-9a-z]+)(\/#)?$/; 8 | const plurkUserMatcher: RegExp = /plurk\.com\/(m\/u\/)?([0-9a-zA-Z_]+)(\/#)?$/; 9 | 10 | /** 11 | * Extracts plurk id from URL provided. 12 | * @param url Url to parse. 13 | * @param decode Should automatic converts base36 id to number? 14 | * @return {string | undefined} 15 | */ 16 | export function plurk(url: string, decode: false): string | undefined; 17 | /** 18 | * Extracts plurk id from URL provided. 19 | * @param url Url to parse. 20 | * @param decode Should automatic converts base36 id to number? 21 | * @return {number | undefined} 22 | */ 23 | export function plurk(url: string, decode: true): number | undefined; 24 | export function plurk(url: string, decode: boolean): string | number | undefined { 25 | const result: string[] | null = plurkUrlMatcher.exec(url); 26 | if(result) { 27 | const id: string = result[2]; 28 | if(id) return decode ? base36.decode(id) : id; 29 | } 30 | } 31 | 32 | /** 33 | * Extracts user id from URL provided. 34 | * @param url Url to parse. 35 | * @param decode Should automatic converts base36 id to number? 36 | * @return {string | undefined} 37 | */ 38 | export function user(url: string, decode: false): string | undefined; 39 | /** 40 | * Extracts user id from URL provided. 41 | * @param url Url to parse. 42 | * @param decode Should automatic converts base36 id to number? 43 | * @return {number | undefined} 44 | */ 45 | export function user(url: string, decode: true): number | undefined; 46 | export function user(url: string, decode: boolean): string | number | undefined { 47 | const result: string[] | null = plurkUserMatcher.exec(url); 48 | if(result) { 49 | const id: string = result[2]; 50 | if(id) return decode ? base36.decode(id) : id; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/api-structs.ts: -------------------------------------------------------------------------------- 1 | export namespace APIStructs { 2 | export const enum YesNo { 3 | no = 0, 4 | yes = 1, 5 | } 6 | 7 | export const enum BirthdayPrivacy { 8 | hideAll = 0, 9 | hideYear = 1, 10 | showAll = 2, 11 | } 12 | 13 | export const enum Gender { 14 | female = 0, 15 | male = 1, 16 | notStatingOrOther = 2, 17 | } 18 | 19 | export const enum Relationship { 20 | notSaying = 'not_saying', 21 | single = 'single', 22 | married = 'married', 23 | divorced = 'divorced', 24 | engaged = 'engaged', 25 | inRelationship = 'in_relationship', 26 | complicated = 'complicated', 27 | widowed = 'widowed', 28 | unstableRelationship = 'unstable_relationship', 29 | openRelationship = 'open_relationship' 30 | } 31 | 32 | export const enum CommentableState { 33 | unlimited = 0, 34 | disabledComments = 1, 35 | friendsOnly = 2, 36 | } 37 | 38 | export interface User { 39 | id: number; 40 | nick_name: string; 41 | display_name: string; 42 | premium: YesNo; 43 | has_profile_image: YesNo; 44 | avatar: string | null; 45 | location: string; 46 | default_lang: string; 47 | date_of_birth: Date; 48 | bday_privacy: BirthdayPrivacy; 49 | full_name: string; 50 | gender: Gender; 51 | karma: number; 52 | recruited: number; 53 | relationship: Relationship; 54 | } 55 | 56 | export interface Entry { 57 | id: number; 58 | plurk_id: number; 59 | user_id: number; 60 | user?: User; 61 | lang: string; 62 | posted: Date; 63 | last_edited: Date | null; 64 | qualifier: string; 65 | qualifier_translated: string; 66 | content: string; 67 | content_raw: string; 68 | } 69 | 70 | export interface Plurk extends Entry { 71 | owner_id: number; 72 | owner?: User; 73 | is_unread: YesNo; 74 | no_comments: CommentableState; 75 | plurk_type: number; 76 | response_count: number; 77 | responses_seen: number; 78 | limited_to: number[] | null; 79 | limited_to_data?: User[]; 80 | favorite: boolean; 81 | favorite_count: number; 82 | favorers: number[]; 83 | favorers_data?: User[]; 84 | replurkable: boolean; 85 | replurked: boolean; 86 | replurker_id: number; 87 | replurker?: User; 88 | replurkers_count: number; 89 | replurkers: number[]; 90 | replurkers_data?: User[]; 91 | } 92 | 93 | export interface Response extends Entry { 94 | 95 | } 96 | 97 | export const enum AlertType { 98 | friendshipRequest = 'friendship_request', 99 | friendshipPending = 'friendship_pending', 100 | newFan = 'new_fan', 101 | friendshipAccepted = 'friendship_accepted', 102 | newFriend = 'new_friend', 103 | privatePlurk = 'private_plurk', 104 | plurkLiked = 'plurk_liked', 105 | plurkReplurked = 'plurk_replurked', 106 | mentioned = 'mentioned', 107 | myResponded = 'my_responded', 108 | } 109 | 110 | export interface Alert { 111 | type: AlertType; 112 | } 113 | 114 | export interface MentionedAlert extends Alert { 115 | type: AlertType.mentioned; 116 | from_user: User; 117 | posted: Date; 118 | plurk_id: number; 119 | num_others: number; 120 | response_id: number | null; 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /lib/api-structs.d.ts: -------------------------------------------------------------------------------- 1 | export declare namespace APIStructs { 2 | const enum YesNo { 3 | no = 0, 4 | yes = 1 5 | } 6 | const enum BirthdayPrivacy { 7 | hideAll = 0, 8 | hideYear = 1, 9 | showAll = 2 10 | } 11 | const enum Gender { 12 | female = 0, 13 | male = 1, 14 | notStatingOrOther = 2 15 | } 16 | const enum Relationship { 17 | notSaying = "not_saying", 18 | single = "single", 19 | married = "married", 20 | divorced = "divorced", 21 | engaged = "engaged", 22 | inRelationship = "in_relationship", 23 | complicated = "complicated", 24 | widowed = "widowed", 25 | unstableRelationship = "unstable_relationship", 26 | openRelationship = "open_relationship" 27 | } 28 | const enum CommentableState { 29 | unlimited = 0, 30 | disabledComments = 1, 31 | friendsOnly = 2 32 | } 33 | interface User { 34 | id: number; 35 | nick_name: string; 36 | display_name: string; 37 | premium: YesNo; 38 | has_profile_image: YesNo; 39 | avatar: string | null; 40 | location: string; 41 | default_lang: string; 42 | date_of_birth: Date; 43 | bday_privacy: BirthdayPrivacy; 44 | full_name: string; 45 | gender: Gender; 46 | karma: number; 47 | recruited: number; 48 | relationship: Relationship; 49 | } 50 | interface Entry { 51 | id: number; 52 | plurk_id: number; 53 | user_id: number; 54 | user?: User; 55 | lang: string; 56 | posted: Date; 57 | last_edited: Date | null; 58 | qualifier: string; 59 | qualifier_translated: string; 60 | content: string; 61 | content_raw: string; 62 | } 63 | interface Plurk extends Entry { 64 | owner_id: number; 65 | owner?: User; 66 | is_unread: YesNo; 67 | no_comments: CommentableState; 68 | plurk_type: number; 69 | response_count: number; 70 | responses_seen: number; 71 | limited_to: number[] | null; 72 | limited_to_data?: User[]; 73 | favorite: boolean; 74 | favorite_count: number; 75 | favorers: number[]; 76 | favorers_data?: User[]; 77 | replurkable: boolean; 78 | replurked: boolean; 79 | replurker_id: number; 80 | replurker?: User; 81 | replurkers_count: number; 82 | replurkers: number[]; 83 | replurkers_data?: User[]; 84 | } 85 | interface Response extends Entry { 86 | } 87 | const enum AlertType { 88 | friendshipRequest = "friendship_request", 89 | friendshipPending = "friendship_pending", 90 | newFan = "new_fan", 91 | friendshipAccepted = "friendship_accepted", 92 | newFriend = "new_friend", 93 | privatePlurk = "private_plurk", 94 | plurkLiked = "plurk_liked", 95 | plurkReplurked = "plurk_replurked", 96 | mentioned = "mentioned", 97 | myResponded = "my_responded" 98 | } 99 | interface Alert { 100 | type: AlertType; 101 | } 102 | interface MentionedAlert extends Alert { 103 | type: AlertType.mentioned; 104 | from_user: User; 105 | posted: Date; 106 | plurk_id: number; 107 | num_others: number; 108 | response_id: number | null; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 5 | "module": "commonjs", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | "lib": ["es5", "es6"], /* Specify library files to be included in the compilation: */ 7 | // "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 11 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 12 | // "outFile": "./", /* Concatenate and emit output to single file. */ 13 | "outDir": "lib", /* Redirect output structure to the directory. */ 14 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 15 | // "removeComments": true, /* Do not emit comments to output. */ 16 | // "noEmit": true, /* Do not emit outputs. */ 17 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 18 | "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 19 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 20 | 21 | /* Strict Type-Checking Options */ 22 | "strict": true, /* Enable all strict type-checking options. */ 23 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 24 | // "strictNullChecks": true, /* Enable strict null checks. */ 25 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 26 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 27 | 28 | /* Additional Checks */ 29 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 30 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 31 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 32 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 33 | 34 | /* Module Resolution Options */ 35 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 36 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 37 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 38 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 39 | "typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */ 40 | "types": ["node", "bluebird"] /* Type declaration files to be included in compilation. */ 41 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 42 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 43 | 44 | /* Source Map Options */ 45 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 46 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 47 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 48 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 49 | 50 | /* Experimental Options */ 51 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 52 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 53 | }, 54 | "include": [ 55 | "./src/**/*" 56 | ] 57 | } -------------------------------------------------------------------------------- /src/api-utils.ts: -------------------------------------------------------------------------------- 1 | import * as request from 'request-promise'; 2 | import { readFile, writeFile } from 'fs'; 3 | import { join as joinPath } from 'path'; 4 | import { promisify } from 'util'; 5 | 6 | const readFileAsync = promisify(readFile); 7 | const writeFileAsync = promisify(writeFile); 8 | 9 | const API_ENDPOINT = 'https://www.plurk.com/API/2/list'; 10 | 11 | const WRITE_PATH = joinPath(__dirname, '../src/api-parameters.ts'); 12 | 13 | const isTruthy = Boolean as Function as (value: T) => value is Exclude; 14 | 15 | function mapValues(val: string): string { 16 | switch(val.trim()) { 17 | case 'true': 18 | case 'false': 19 | case 'null': 20 | return val; 21 | case 'PID': 22 | return 'number'; 23 | default: 24 | if(Number.isFinite(Number.parseFloat(val))) 25 | return val; 26 | return `'${escapeUnsaveCharacters(val)}'`; 27 | } 28 | } 29 | 30 | const unsafeCharacters = /[\0\n\f\n\r\t\v\\'"]/mg; 31 | 32 | function escapeUnsaveCharacters(str: string) { 33 | return str.replace(unsafeCharacters, escapeUnsaveCharactersMap); 34 | } 35 | 36 | function escapeUnsaveCharactersMap(src: string) { 37 | switch(src) { 38 | case '\r': return '\\r'; 39 | case '\n': return '\\n'; 40 | case '\b': return '\\b'; 41 | case '\t': return '\\t'; 42 | case '\v': return '\\v'; 43 | case '\0': return '\\0'; 44 | case '\\': return '\\\\'; 45 | case '\'': return '\\\''; 46 | case '\"': return '\\\"'; 47 | } 48 | return src; 49 | } 50 | 51 | function processOptions(options: string, defaultValue: string) { 52 | if(options) 53 | return options.split('|').map(mapValues).filter(isTruthy).join(' | '); 54 | if(!defaultValue) 55 | return 'any'; 56 | switch(defaultValue.trim()) { 57 | case 'true': 58 | case 'false': 59 | return 'boolean'; 60 | case 'null': 61 | case 'n/a': 62 | return 'any'; 63 | case '[]': 64 | return 'any[]'; 65 | default: 66 | if(Number.isFinite(Number.parseFloat(defaultValue))) 67 | return 'number'; 68 | return 'string'; 69 | } 70 | } 71 | 72 | function toCapitalize(str: string) { 73 | return str.length ? str.charAt(0).toUpperCase() + str.substring(1) : str; 74 | } 75 | 76 | const colMatcher = /^(\w+)(?:=([^\|:]+(?:\|[^\|:]+)*))?(?:\:(.+))?$/; 77 | 78 | function dropOptionsToNamespaces(namespaces: Map, path: string, data: string[]): string { 79 | let pathIdx = path.indexOf('/'); 80 | if(pathIdx >= 0) { 81 | const ns = toCapitalize(path.substring(0, pathIdx)); 82 | let child = namespaces.get(ns); 83 | if(!child) { 84 | child = new Map(); 85 | namespaces.set(ns, child); 86 | } 87 | return `${ns}.${dropOptionsToNamespaces(child, path.substring(pathIdx + 1), data)}`; 88 | } else { 89 | const results: string[] = []; 90 | for(const type of data) { 91 | const m = colMatcher.exec(type); 92 | if(!m) { 93 | console.error('WARN: %m does not matches the regexp.', type); 94 | continue; 95 | } 96 | const [, param, options, defaultValue] = m; 97 | results.push( 98 | `${ 99 | param 100 | }${ 101 | defaultValue ? '?' : '' 102 | }: ${ 103 | processOptions(options, defaultValue) 104 | };`, 105 | ); 106 | } 107 | const ns = `${toCapitalize(path)}Options`; 108 | namespaces.set(ns, results); 109 | return ns; 110 | } 111 | } 112 | 113 | function printNamespaces(out: string[], namespaces: Map, indent = '') { 114 | let isFirst = true; 115 | for(const [name, data] of namespaces) { 116 | if(!isFirst) out.push(''); 117 | if(Array.isArray(data)) { 118 | out.push(`${indent}export interface ${name} {`); 119 | for(const d of data) 120 | out.push(`${indent} ${d}`); 121 | } else { 122 | out.push(`${indent}export namespace ${name} {`); 123 | printNamespaces(out, data, indent + ' '); 124 | } 125 | out.push(`${indent}}`); 126 | isFirst = false; 127 | } 128 | } 129 | 130 | async function generateAPIMap() { 131 | const apiMap: { 132 | [api: string]: string[]; 133 | } = await request(API_ENDPOINT, { 134 | json: true, 135 | }); 136 | const namespaces = new Map(); 137 | const packageJson = JSON.parse(await readFileAsync(joinPath(__dirname, '../package.json'), 'utf8')); 138 | const results: string[] = [ 139 | `// Generate using ${packageJson?.name}, data from ${API_ENDPOINT}`, 140 | `// Do not edit manually!`, 141 | '', 142 | 'export interface APIParameters {', 143 | ' [api: string]: [any, any];', 144 | ]; 145 | for(const k in apiMap) { 146 | if(!(k in apiMap)) 147 | continue; 148 | const path = k.replace('/APP/', ''); 149 | results.push( 150 | ` '${escapeUnsaveCharacters(path)}': [`, 151 | ` ${dropOptionsToNamespaces(namespaces, path, apiMap[k])},`, 152 | ' any,', 153 | ' ];' 154 | ); 155 | } 156 | results.push('}', ''); 157 | printNamespaces(results, namespaces); 158 | await writeFileAsync(WRITE_PATH, `${results.join('\n')}\n`); 159 | } 160 | 161 | generateAPIMap().catch( 162 | reason => console.error(reason.stack || reason), 163 | ); 164 | -------------------------------------------------------------------------------- /lib/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import { EventEmitter } from 'events'; 3 | import * as request from 'request-promise'; 4 | import { limitTo } from './limit-to'; 5 | import { APIParameters } from './api-parameters'; 6 | /** 7 | * `PlurkClient` is a class that wraps all plurk API call and handles comet channel when enabled. 8 | * It inherits from Node.js's [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) class. 9 | */ 10 | export declare class PlurkClient extends EventEmitter implements IPlurkClientEventEmitter { 11 | /** 12 | * Consumer token, can be obtain one from Plurk App console. 13 | */ 14 | consumerKey: string; 15 | /** 16 | * Consumer token secret, should be together with consumer token. 17 | */ 18 | consumerSecret: string; 19 | /** 20 | * OAuth access token or request token of current client. 21 | */ 22 | token: string; 23 | /** 24 | * OAuth token secret of current client. 25 | */ 26 | tokenSecret: string; 27 | /** 28 | * Flag indicates if the commet channel is started. 29 | */ 30 | cometStarted: boolean; 31 | /** 32 | * Boolean field, set to `true` to automatic stops the 33 | * comet channel when any error has been thrown, 34 | * or else it will keep reconnect even have errors. 35 | */ 36 | stopCometOnError: boolean; 37 | /** 38 | * Boolean field, set to `true` to populate the user data 39 | * to specific fields. For example, response data in comet channel 40 | * will have a `user` field with user details in it if detail of `user_id` 41 | * is found in raw channel response. 42 | */ 43 | populateUsers: boolean; 44 | private _cometUrl?; 45 | private _pollCometRequest?; 46 | /** 47 | * Constructor 48 | * @param consumerKey Consumer token, can be obtain one from Plurk App console. 49 | * @param consumerSecret Consumer token secret, should be together with consumer token. 50 | * @param token Oauth access token, optional. 51 | * You may assign it here or use `getRequestToken()` and then `getAccessToken()` 52 | * to obtain one from user with oauth authentication flow. 53 | * @param tokenSecret Oauth access token secret, optional. Also this should be come with access token. 54 | */ 55 | constructor(consumerKey: string, consumerSecret: string, token?: string, tokenSecret?: string); 56 | /** 57 | * Get oauth request token (temporary) for user to authenticate. 58 | * It will assigns `token` and `tokenSecret` of current instance for further process. 59 | * @param callback Redirect URL after authenticate success, can be omitted if this is not a web app. 60 | * @return {PromiseLike.} Current plurk client instance. 61 | */ 62 | getRequestToken(callback?: string): Promise; 63 | /** 64 | * Get oauth access token (permanent) for requesting other API. 65 | * It will assigns `token` and `tokenSecret` of current instance. 66 | * Should be called once users' verifier has been received. 67 | * @param verifier The oauth verifier received from the user. 68 | * @return {PromiseLike.} Current plurk client instance. 69 | */ 70 | getAccessToken(verifier: string): Promise; 71 | /** 72 | * Make a post request API call to Plurk (as recommended in the documentation), 73 | * it will uses the oauth token provided in the client instance if available. 74 | * [Plurk API Reference](https://www.plurk.com/API) 75 | * @param api API path as written in the documentation. 76 | * `APP/` prefix can be omitted. 77 | * @param parameters Object hash of the parameters, can be omitted if no parameters. 78 | * Also it will automatically converts `Date` object entries to 79 | * [ISO8601 string](https://en.wikipedia.org/wiki/ISO_8601) and 80 | * `Array` object entries to JSON string before request. 81 | * You may also pass Node.js native `Buffer` or `Stream` values for image uploading APIs 82 | * such as [`/APP/Timeline/uploadPicture`](https://www.plurk.com/API#/APP/Timeline/uploadPicture). 83 | * @return {Promise.} The parsed JSON data respond from Plurk. 84 | * It will auto converts all known date/time fields to `Date` objects 85 | * and `limited_to` field to array of numbers. 86 | * Also, the response will return some timing measurement info for the call, for details please see 87 | * [the usage of the request package](https://github.com/request/request/blob/master/README.md) 88 | */ 89 | request(api: K, parameters?: APIParameters[K][0]): request.RequestPromise & PromiseLike; 90 | /** 91 | * Start long poll from comet channel, it auto handles request for comet server 92 | * URL and it will auto keep polling until you stops it. 93 | */ 94 | startComet(): void; 95 | /** 96 | * Stops long poll from comet channel. 97 | */ 98 | stopComet(): void; 99 | /** 100 | * Restart long poll from comet channel. 101 | * Normally this method is automatically called while polling. 102 | */ 103 | pollComet(): void; 104 | /** 105 | * User authentication URL. Should be inform user to navigate to this URL 106 | * once the promise of `getRequestToken(...)` has been resolved. 107 | */ 108 | get authPage(): string; 109 | /** 110 | * Mobile version of user authentication URL. 111 | * Users may navigate to this URL instead of `authPage` if they are using smartphones. 112 | */ 113 | get mobileAuthPage(): string; 114 | private _getOAuthParams; 115 | private _setOAuthParams; 116 | } 117 | export interface IPlurkClientEventEmitter extends NodeJS.EventEmitter { 118 | /** 119 | * Event callback on comet channel responses. 120 | * This will be called even on comet channel does not returns any new push. 121 | * This event fires before any other comet channel events. 122 | * The fields are already converted to JavaScript types just like 123 | * the resolved promise in `request()`. 124 | */ 125 | on(event: 'comet', listener: (comet: any, raw: string) => void): this; 126 | /** 127 | * General error callback from comet channel. 128 | */ 129 | on(event: 'error', listener: (err: any) => void): this; 130 | /** 131 | * Event callback on comet channel receives each data push. 132 | * It is already filtered by push event types as you defined in `event` parameter 133 | * and converted to JavaScript types just like the resolved promise in `request()`. 134 | * For more info please check out 135 | * [comet channel specification](https://www.plurk.com/API#Comet_channel_specification) 136 | * section in API reference. 137 | * @param event Can be `new_plurk`, `new_response` or any others which supported. 138 | */ 139 | on(event: string, listener: (data: any) => void): this; 140 | } 141 | export * from './urlwatch'; 142 | export * from './base36'; 143 | export { limitTo }; 144 | -------------------------------------------------------------------------------- /lib/api-utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __generator = (this && this.__generator) || function (thisArg, body) { 12 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 13 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 | function verb(n) { return function (v) { return step([n, v]); }; } 15 | function step(op) { 16 | if (f) throw new TypeError("Generator is already executing."); 17 | while (_) try { 18 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 | if (y = 0, t) op = [op[0] & 2, t.value]; 20 | switch (op[0]) { 21 | case 0: case 1: t = op; break; 22 | case 4: _.label++; return { value: op[1], done: false }; 23 | case 5: _.label++; y = op[1]; op = [0]; continue; 24 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 | default: 26 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 | if (t[2]) _.ops.pop(); 31 | _.trys.pop(); continue; 32 | } 33 | op = body.call(thisArg, _); 34 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 | } 37 | }; 38 | var __values = (this && this.__values) || function(o) { 39 | var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; 40 | if (m) return m.call(o); 41 | if (o && typeof o.length === "number") return { 42 | next: function () { 43 | if (o && i >= o.length) o = void 0; 44 | return { value: o && o[i++], done: !o }; 45 | } 46 | }; 47 | throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); 48 | }; 49 | var __read = (this && this.__read) || function (o, n) { 50 | var m = typeof Symbol === "function" && o[Symbol.iterator]; 51 | if (!m) return o; 52 | var i = m.call(o), r, ar = [], e; 53 | try { 54 | while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); 55 | } 56 | catch (error) { e = { error: error }; } 57 | finally { 58 | try { 59 | if (r && !r.done && (m = i["return"])) m.call(i); 60 | } 61 | finally { if (e) throw e.error; } 62 | } 63 | return ar; 64 | }; 65 | Object.defineProperty(exports, "__esModule", { value: true }); 66 | var request = require("request-promise"); 67 | var fs_1 = require("fs"); 68 | var path_1 = require("path"); 69 | var util_1 = require("util"); 70 | var readFileAsync = util_1.promisify(fs_1.readFile); 71 | var writeFileAsync = util_1.promisify(fs_1.writeFile); 72 | var API_ENDPOINT = 'https://www.plurk.com/API/2/list'; 73 | var WRITE_PATH = path_1.join(__dirname, '../src/api-parameters.ts'); 74 | var isTruthy = Boolean; 75 | function mapValues(val) { 76 | switch (val.trim()) { 77 | case 'true': 78 | case 'false': 79 | case 'null': 80 | return val; 81 | case 'PID': 82 | return 'number'; 83 | default: 84 | if (Number.isFinite(Number.parseFloat(val))) 85 | return val; 86 | return "'" + escapeUnsaveCharacters(val) + "'"; 87 | } 88 | } 89 | var unsafeCharacters = /[\0\n\f\n\r\t\v\\'"]/mg; 90 | function escapeUnsaveCharacters(str) { 91 | return str.replace(unsafeCharacters, escapeUnsaveCharactersMap); 92 | } 93 | function escapeUnsaveCharactersMap(src) { 94 | switch (src) { 95 | case '\r': return '\\r'; 96 | case '\n': return '\\n'; 97 | case '\b': return '\\b'; 98 | case '\t': return '\\t'; 99 | case '\v': return '\\v'; 100 | case '\0': return '\\0'; 101 | case '\\': return '\\\\'; 102 | case '\'': return '\\\''; 103 | case '\"': return '\\\"'; 104 | } 105 | return src; 106 | } 107 | function processOptions(options, defaultValue) { 108 | if (options) 109 | return options.split('|').map(mapValues).filter(isTruthy).join(' | '); 110 | if (!defaultValue) 111 | return 'any'; 112 | switch (defaultValue.trim()) { 113 | case 'true': 114 | case 'false': 115 | return 'boolean'; 116 | case 'null': 117 | case 'n/a': 118 | return 'any'; 119 | case '[]': 120 | return 'any[]'; 121 | default: 122 | if (Number.isFinite(Number.parseFloat(defaultValue))) 123 | return 'number'; 124 | return 'string'; 125 | } 126 | } 127 | function toCapitalize(str) { 128 | return str.length ? str.charAt(0).toUpperCase() + str.substring(1) : str; 129 | } 130 | var colMatcher = /^(\w+)(?:=([^\|:]+(?:\|[^\|:]+)*))?(?:\:(.+))?$/; 131 | function dropOptionsToNamespaces(namespaces, path, data) { 132 | var e_1, _a; 133 | var pathIdx = path.indexOf('/'); 134 | if (pathIdx >= 0) { 135 | var ns = toCapitalize(path.substring(0, pathIdx)); 136 | var child = namespaces.get(ns); 137 | if (!child) { 138 | child = new Map(); 139 | namespaces.set(ns, child); 140 | } 141 | return ns + "." + dropOptionsToNamespaces(child, path.substring(pathIdx + 1), data); 142 | } 143 | else { 144 | var results = []; 145 | try { 146 | for (var data_1 = __values(data), data_1_1 = data_1.next(); !data_1_1.done; data_1_1 = data_1.next()) { 147 | var type = data_1_1.value; 148 | var m = colMatcher.exec(type); 149 | if (!m) { 150 | console.error('WARN: %m does not matches the regexp.', type); 151 | continue; 152 | } 153 | var _b = __read(m, 4), param = _b[1], options = _b[2], defaultValue = _b[3]; 154 | results.push("" + param + (defaultValue ? '?' : '') + ": " + processOptions(options, defaultValue) + ";"); 155 | } 156 | } 157 | catch (e_1_1) { e_1 = { error: e_1_1 }; } 158 | finally { 159 | try { 160 | if (data_1_1 && !data_1_1.done && (_a = data_1.return)) _a.call(data_1); 161 | } 162 | finally { if (e_1) throw e_1.error; } 163 | } 164 | var ns = toCapitalize(path) + "Options"; 165 | namespaces.set(ns, results); 166 | return ns; 167 | } 168 | } 169 | function printNamespaces(out, namespaces, indent) { 170 | var e_2, _a, e_3, _b; 171 | if (indent === void 0) { indent = ''; } 172 | var isFirst = true; 173 | try { 174 | for (var namespaces_1 = __values(namespaces), namespaces_1_1 = namespaces_1.next(); !namespaces_1_1.done; namespaces_1_1 = namespaces_1.next()) { 175 | var _c = __read(namespaces_1_1.value, 2), name = _c[0], data = _c[1]; 176 | if (!isFirst) 177 | out.push(''); 178 | if (Array.isArray(data)) { 179 | out.push(indent + "export interface " + name + " {"); 180 | try { 181 | for (var data_2 = (e_3 = void 0, __values(data)), data_2_1 = data_2.next(); !data_2_1.done; data_2_1 = data_2.next()) { 182 | var d = data_2_1.value; 183 | out.push(indent + " " + d); 184 | } 185 | } 186 | catch (e_3_1) { e_3 = { error: e_3_1 }; } 187 | finally { 188 | try { 189 | if (data_2_1 && !data_2_1.done && (_b = data_2.return)) _b.call(data_2); 190 | } 191 | finally { if (e_3) throw e_3.error; } 192 | } 193 | } 194 | else { 195 | out.push(indent + "export namespace " + name + " {"); 196 | printNamespaces(out, data, indent + ' '); 197 | } 198 | out.push(indent + "}"); 199 | isFirst = false; 200 | } 201 | } 202 | catch (e_2_1) { e_2 = { error: e_2_1 }; } 203 | finally { 204 | try { 205 | if (namespaces_1_1 && !namespaces_1_1.done && (_a = namespaces_1.return)) _a.call(namespaces_1); 206 | } 207 | finally { if (e_2) throw e_2.error; } 208 | } 209 | } 210 | function generateAPIMap() { 211 | return __awaiter(this, void 0, void 0, function () { 212 | var apiMap, namespaces, packageJson, _a, _b, results, k, path; 213 | return __generator(this, function (_c) { 214 | switch (_c.label) { 215 | case 0: return [4 /*yield*/, request(API_ENDPOINT, { 216 | json: true, 217 | })]; 218 | case 1: 219 | apiMap = _c.sent(); 220 | namespaces = new Map(); 221 | _b = (_a = JSON).parse; 222 | return [4 /*yield*/, readFileAsync(path_1.join(__dirname, '../package.json'), 'utf8')]; 223 | case 2: 224 | packageJson = _b.apply(_a, [_c.sent()]); 225 | results = [ 226 | "// Generate using " + (packageJson === null || packageJson === void 0 ? void 0 : packageJson.name) + ", data from " + API_ENDPOINT, 227 | "// Do not edit manually!", 228 | '', 229 | 'export interface APIParameters {', 230 | ' [api: string]: [any, any];', 231 | ]; 232 | for (k in apiMap) { 233 | if (!(k in apiMap)) 234 | continue; 235 | path = k.replace('/APP/', ''); 236 | results.push(" '" + escapeUnsaveCharacters(path) + "': [", " " + dropOptionsToNamespaces(namespaces, path, apiMap[k]) + ",", ' any,', ' ];'); 237 | } 238 | results.push('}', ''); 239 | printNamespaces(results, namespaces); 240 | return [4 /*yield*/, writeFileAsync(WRITE_PATH, results.join('\n') + "\n")]; 241 | case 3: 242 | _c.sent(); 243 | return [2 /*return*/]; 244 | } 245 | }); 246 | }); 247 | } 248 | generateAPIMap().catch(function (reason) { return console.error(reason.stack || reason); }); 249 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import { parse as parseUrl, Url } from 'url'; 3 | import { parse as parseBody } from 'querystring'; 4 | import { EventEmitter } from 'events'; 5 | import * as BlueBirdPromise from 'bluebird'; 6 | import { OAuthOptions } from 'request'; 7 | import * as request from 'request-promise'; 8 | import { limitTo } from './limit-to'; 9 | import { APIStructs } from './api-structs'; 10 | import { APIParameters } from './api-parameters'; 11 | 12 | const endPoint: string = 'https://www.plurk.com/'; 13 | const requestTokenUrl: string = `${endPoint}OAuth/request_token`; 14 | const accessTokenUrl: string = `${endPoint}OAuth/access_token`; 15 | 16 | const pathMatcher: RegExp = /^\/?(?:APP\/)?(.+)$/; 17 | 18 | /** 19 | * `PlurkClient` is a class that wraps all plurk API call and handles comet channel when enabled. 20 | * It inherits from Node.js's [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) class. 21 | */ 22 | export class PlurkClient extends EventEmitter implements IPlurkClientEventEmitter { 23 | /** 24 | * Consumer token, can be obtain one from Plurk App console. 25 | */ 26 | consumerKey: string; 27 | /** 28 | * Consumer token secret, should be together with consumer token. 29 | */ 30 | consumerSecret: string; 31 | /** 32 | * OAuth access token or request token of current client. 33 | */ 34 | token: string; 35 | /** 36 | * OAuth token secret of current client. 37 | */ 38 | tokenSecret: string; 39 | /** 40 | * Flag indicates if the commet channel is started. 41 | */ 42 | cometStarted: boolean = false; 43 | /** 44 | * Boolean field, set to `true` to automatic stops the 45 | * comet channel when any error has been thrown, 46 | * or else it will keep reconnect even have errors. 47 | */ 48 | stopCometOnError: boolean = false; 49 | /** 50 | * Boolean field, set to `true` to populate the user data 51 | * to specific fields. For example, response data in comet channel 52 | * will have a `user` field with user details in it if detail of `user_id` 53 | * is found in raw channel response. 54 | */ 55 | populateUsers: boolean = false; 56 | private _cometUrl?: Url; 57 | private _pollCometRequest?: BlueBirdPromise; 58 | 59 | /** 60 | * Constructor 61 | * @param consumerKey Consumer token, can be obtain one from Plurk App console. 62 | * @param consumerSecret Consumer token secret, should be together with consumer token. 63 | * @param token Oauth access token, optional. 64 | * You may assign it here or use `getRequestToken()` and then `getAccessToken()` 65 | * to obtain one from user with oauth authentication flow. 66 | * @param tokenSecret Oauth access token secret, optional. Also this should be come with access token. 67 | */ 68 | constructor(consumerKey: string, consumerSecret: string, 69 | token: string = '', tokenSecret: string = '') { 70 | super(); 71 | this.consumerKey = consumerKey; 72 | this.consumerSecret = consumerSecret; 73 | this.token = token; 74 | this.tokenSecret = tokenSecret; 75 | } 76 | 77 | /** 78 | * Get oauth request token (temporary) for user to authenticate. 79 | * It will assigns `token` and `tokenSecret` of current instance for further process. 80 | * @param callback Redirect URL after authenticate success, can be omitted if this is not a web app. 81 | * @return {PromiseLike.} Current plurk client instance. 82 | */ 83 | async getRequestToken(callback: string = ''): Promise { 84 | const body = await request({ 85 | method: 'POST', url: requestTokenUrl, 86 | oauth: this._getOAuthParams({ callback }) 87 | }); 88 | return this._setOAuthParams(body); 89 | } 90 | 91 | /** 92 | * Get oauth access token (permanent) for requesting other API. 93 | * It will assigns `token` and `tokenSecret` of current instance. 94 | * Should be called once users' verifier has been received. 95 | * @param verifier The oauth verifier received from the user. 96 | * @return {PromiseLike.} Current plurk client instance. 97 | */ 98 | async getAccessToken(verifier: string): Promise { 99 | const body = await request({ 100 | method: 'POST', url: accessTokenUrl, 101 | oauth: this._getOAuthParams({ verifier }) 102 | }); 103 | return this._setOAuthParams(body); 104 | } 105 | 106 | /** 107 | * Make a post request API call to Plurk (as recommended in the documentation), 108 | * it will uses the oauth token provided in the client instance if available. 109 | * [Plurk API Reference](https://www.plurk.com/API) 110 | * @param api API path as written in the documentation. 111 | * `APP/` prefix can be omitted. 112 | * @param parameters Object hash of the parameters, can be omitted if no parameters. 113 | * Also it will automatically converts `Date` object entries to 114 | * [ISO8601 string](https://en.wikipedia.org/wiki/ISO_8601) and 115 | * `Array` object entries to JSON string before request. 116 | * You may also pass Node.js native `Buffer` or `Stream` values for image uploading APIs 117 | * such as [`/APP/Timeline/uploadPicture`](https://www.plurk.com/API#/APP/Timeline/uploadPicture). 118 | * @return {Promise.} The parsed JSON data respond from Plurk. 119 | * It will auto converts all known date/time fields to `Date` objects 120 | * and `limited_to` field to array of numbers. 121 | * Also, the response will return some timing measurement info for the call, for details please see 122 | * [the usage of the request package](https://github.com/request/request/blob/master/README.md) 123 | */ 124 | request( 125 | api: K, 126 | parameters?: APIParameters[K][0], 127 | ): request.RequestPromise & PromiseLike; 128 | request(api: string, parameters?: any): request.RequestPromise { 129 | const resolved: string[] | null = pathMatcher.exec(api); 130 | if(!resolved || resolved.length < 2) 131 | throw new Error(`Invalid api path '${api}'`); 132 | const form: any = {}; 133 | let useFormData: boolean = false; 134 | if(parameters) 135 | for(let key in parameters) { 136 | const value: any = parameters[key]; 137 | switch(typeof value) { 138 | case 'undefined': case 'function': case 'symbol': break; 139 | case 'object': 140 | if(value instanceof Date) 141 | form[key] = value.toISOString(); 142 | else if(value && (value instanceof Buffer || typeof value.pipe === 'function')) { 143 | form[key] = value; 144 | useFormData = true; 145 | } else 146 | form[key] = JSON.stringify(value); 147 | break; 148 | default: 149 | form[key] = value; 150 | break; 151 | } 152 | } 153 | return request({ 154 | url: `${endPoint}APP/${resolved[1]}`, 155 | [useFormData ? 'formData' : 'form']: form, 156 | method: 'POST', json: true, 157 | jsonReviver: PlurkClientUtils.parseResponse, 158 | headers: { 159 | 'Content-Type': useFormData ? 160 | 'multipart/form-data' : 161 | 'application/x-www-form-urlencoded', 162 | }, 163 | oauth: this._getOAuthParams(), 164 | time: true, 165 | transform: transformWithTiming, 166 | }); 167 | } 168 | 169 | /** 170 | * Start long poll from comet channel, it auto handles request for comet server 171 | * URL and it will auto keep polling until you stops it. 172 | */ 173 | startComet(): void { 174 | if(this.cometStarted) return; 175 | this.cometStarted = true; 176 | this.request('Realtime/getUserChannel') 177 | .then((data: any): void => { 178 | if(!data.comet_server) 179 | throw new Error('Comet URL not found'); 180 | this._cometUrl = parseUrl(data.comet_server, true); 181 | if(this.cometStarted) this.pollComet(); 182 | }) 183 | .catch((err: any): void => { 184 | this.cometStarted = false; 185 | this.emit('error', err); 186 | }); 187 | } 188 | 189 | /** 190 | * Stops long poll from comet channel. 191 | */ 192 | stopComet(): void { 193 | if(!this.cometStarted) return; 194 | this.cometStarted = false; 195 | if(this._pollCometRequest) { 196 | this._pollCometRequest.cancel(); 197 | delete this._pollCometRequest; 198 | } 199 | } 200 | 201 | /** 202 | * Restart long poll from comet channel. 203 | * Normally this method is automatically called while polling. 204 | */ 205 | pollComet(): void { 206 | if(this._pollCometRequest) 207 | this._pollCometRequest.cancel(); 208 | if(!this.cometStarted) return; 209 | if(!this._cometUrl) 210 | throw new Error('Unknown comet url'); 211 | this._pollCometRequest = request({ 212 | url: this._cometUrl, timeout: 60000, 213 | agentOptions: { rejectUnauthorized: false } 214 | }) 215 | .then((response: string): void => { 216 | if(!this._cometUrl) 217 | throw new Error('Unknown comet url'); 218 | const parsedResponse: any = JSON.parse(response.substring( 219 | response.indexOf('{'), 220 | response.lastIndexOf('}') + 1 221 | ), PlurkClientUtils.parseResponse); 222 | this.emit('comet', parsedResponse, response); 223 | const { data, user, new_offset } = parsedResponse; 224 | if(this._cometUrl?.query && typeof this._cometUrl.query !== 'string') 225 | this._cometUrl.query.offset = new_offset; 226 | delete this._cometUrl.search; 227 | if(data && data.length) 228 | for(const entry of data) { 229 | if(this.populateUsers && user) 230 | PlurkClientUtils.populateUsers(entry, user); 231 | if(entry && entry.type) 232 | this.emit(entry.type, entry); 233 | } 234 | process.nextTick(PlurkClientUtils.pollComet, this); 235 | }) 236 | .catch((err: any): void => { 237 | if(this.stopCometOnError) 238 | this.cometStarted = false; 239 | else 240 | process.nextTick(PlurkClientUtils.pollComet, this); 241 | this.emit('error', err); 242 | }) 243 | .finally((): void => { 244 | delete this._pollCometRequest; 245 | }); 246 | } 247 | 248 | /** 249 | * User authentication URL. Should be inform user to navigate to this URL 250 | * once the promise of `getRequestToken(...)` has been resolved. 251 | */ 252 | get authPage(): string { 253 | return this.token ? `${endPoint}OAuth/authorize?oauth_token=${this.token}` : ''; 254 | } 255 | 256 | /** 257 | * Mobile version of user authentication URL. 258 | * Users may navigate to this URL instead of `authPage` if they are using smartphones. 259 | */ 260 | get mobileAuthPage(): string { 261 | return this.token ? `${endPoint}m/authorize?oauth_token=${this.token}` : ''; 262 | } 263 | 264 | private _getOAuthParams(params: OAuthOptions = {}): OAuthOptions { 265 | params.consumer_key = this.consumerKey; 266 | params.consumer_secret = this.consumerSecret; 267 | if(this.token) 268 | params.token = this.token; 269 | if(this.tokenSecret) 270 | params.token_secret = this.tokenSecret; 271 | return params; 272 | } 273 | 274 | private _setOAuthParams(body: string): this { 275 | const val: any = parseBody(body); 276 | if(val.oauth_token) 277 | this.token = val.oauth_token; 278 | if(val.oauth_token_secret) 279 | this.tokenSecret = val.oauth_token_secret; 280 | return this; 281 | } 282 | } 283 | 284 | function transformWithTiming(body: any, response: any, resolveFullResponse?: boolean) { 285 | if(!resolveFullResponse) { 286 | assignIfExists(body, response, 'elapsedTime'); 287 | assignIfExists(body, response, 'responseStartTime'); 288 | assignIfExists(body, response, 'timingStart'); 289 | assignIfExists(body, response, 'timings'); 290 | assignIfExists(body, response, 'timingPhases'); 291 | return body; 292 | } 293 | return response; 294 | } 295 | 296 | function assignIfExists(a: any, b: any, key: PropertyKey) { 297 | if((key in b) && !(key in a)) a[key] = b[key]; 298 | } 299 | 300 | namespace PlurkClientUtils { 301 | const plurkLimitToMatcher: RegExp = /^(?:\|[0-9]+\|)*$/; 302 | 303 | export function pollComet(client: PlurkClient): void { 304 | return client.pollComet(); 305 | } 306 | 307 | export function parseResponse(key: string, value: any): any { 308 | switch(key) { 309 | case 'limited_to': 310 | if(typeof value === 'string' && 311 | plurkLimitToMatcher.test(value)) 312 | return limitTo.parse(value); 313 | break; 314 | case 'date_of_birth': 315 | case 'posted': 316 | case 'now': 317 | case 'issued': 318 | if(typeof value === 'string') 319 | return new Date(value); 320 | break; 321 | case 'timestamp': 322 | if(typeof value === 'number') 323 | return new Date(value * 1000); 324 | break; 325 | } 326 | return value; 327 | } 328 | 329 | export function populateUsers(plurkData: any, users: any): void { 330 | plurkData.owner = users[plurkData.owner_id]; 331 | plurkData.user = users[plurkData.user_id]; 332 | plurkData.replurker = users[plurkData.replurker_id]; 333 | if(Array.isArray(plurkData.limit_to)) 334 | plurkData.limit_to_data = (plurkData.limit_to as number[]).map(populateUsersEntry, users); 335 | if(Array.isArray(plurkData.favorers)) 336 | plurkData.favorers_data = (plurkData.favorers as number[]).map(populateUsersEntry, users); 337 | if(Array.isArray(plurkData.replurkers)) 338 | plurkData.replurkers_data = (plurkData.replurkers as number[]).map(populateUsersEntry, users); 339 | } 340 | 341 | export function populateUsersEntry(this: any, entry: number): any { 342 | return this[entry]; 343 | } 344 | } 345 | 346 | export interface IPlurkClientEventEmitter extends NodeJS.EventEmitter { 347 | /** 348 | * Event callback on comet channel responses. 349 | * This will be called even on comet channel does not returns any new push. 350 | * This event fires before any other comet channel events. 351 | * The fields are already converted to JavaScript types just like 352 | * the resolved promise in `request()`. 353 | */ 354 | on(event: 'comet', listener: (comet: any, raw: string) => void): this; 355 | /** 356 | * General error callback from comet channel. 357 | */ 358 | on(event: 'error', listener: (err: any) => void): this; 359 | /** 360 | * Event callback on comet channel receives each data push. 361 | * It is already filtered by push event types as you defined in `event` parameter 362 | * and converted to JavaScript types just like the resolved promise in `request()`. 363 | * For more info please check out 364 | * [comet channel specification](https://www.plurk.com/API#Comet_channel_specification) 365 | * section in API reference. 366 | * @param event Can be `new_plurk`, `new_response` or any others which supported. 367 | */ 368 | on(event: string, listener: (data: any) => void): this; 369 | } 370 | 371 | export * from './urlwatch'; 372 | export * from './base36'; 373 | export { limitTo }; 374 | -------------------------------------------------------------------------------- /lib/api-parameters.d.ts: -------------------------------------------------------------------------------- 1 | export interface APIParameters { 2 | [api: string]: [any, any]; 3 | 'Users/me': [Users.MeOptions, any]; 4 | 'Users/getKarmaStats': [Users.GetKarmaStatsOptions, any]; 5 | 'Users/update': [Users.UpdateOptions, any]; 6 | 'Users/updateBackground': [Users.UpdateBackgroundOptions, any]; 7 | 'Users/setNameColor': [Users.SetNameColorOptions, any]; 8 | 'Users/getAliases': [Users.GetAliasesOptions, any]; 9 | 'Users/setAlias': [Users.SetAliasOptions, any]; 10 | 'Profile/getOwnProfile': [Profile.GetOwnProfileOptions, any]; 11 | 'Profile/getPublicProfile': [Profile.GetPublicProfileOptions, any]; 12 | 'Polling/getPlurks': [Polling.GetPlurksOptions, any]; 13 | 'Polling/getUnreadCount': [Polling.GetUnreadCountOptions, any]; 14 | 'Timeline/getPlurk': [Timeline.GetPlurkOptions, any]; 15 | 'Timeline/getPlurkCountsInfo': [Timeline.GetPlurkCountsInfoOptions, any]; 16 | 'Timeline/getPlurks': [Timeline.GetPlurksOptions, any]; 17 | 'Timeline/getPublicPlurks': [Timeline.GetPublicPlurksOptions, any]; 18 | 'Timeline/getUnreadPlurks': [Timeline.GetUnreadPlurksOptions, any]; 19 | 'Timeline/plurkAdd': [Timeline.PlurkAddOptions, any]; 20 | 'Timeline/plurkDelete': [Timeline.PlurkDeleteOptions, any]; 21 | 'Timeline/plurkEdit': [Timeline.PlurkEditOptions, any]; 22 | 'Timeline/mutePlurks': [Timeline.MutePlurksOptions, any]; 23 | 'Timeline/unmutePlurks': [Timeline.UnmutePlurksOptions, any]; 24 | 'Timeline/favoritePlurks': [Timeline.FavoritePlurksOptions, any]; 25 | 'Timeline/unfavoritePlurks': [Timeline.UnfavoritePlurksOptions, any]; 26 | 'Timeline/replurk': [Timeline.ReplurkOptions, any]; 27 | 'Timeline/unreplurk': [Timeline.UnreplurkOptions, any]; 28 | 'Timeline/markAsRead': [Timeline.MarkAsReadOptions, any]; 29 | 'Timeline/uploadPicture': [Timeline.UploadPictureOptions, any]; 30 | 'Timeline/toggleComments': [Timeline.ToggleCommentsOptions, any]; 31 | 'Timeline/setPorn': [Timeline.SetPornOptions, any]; 32 | 'Timeline/reportAbuse': [Timeline.ReportAbuseOptions, any]; 33 | 'Timeline/setUnreadSnapshot': [Timeline.SetUnreadSnapshotOptions, any]; 34 | 'Timeline/markAllAsRead': [Timeline.MarkAllAsReadOptions, any]; 35 | 'Responses/get': [Responses.GetOptions, any]; 36 | 'Responses/getById': [Responses.GetByIdOptions, any]; 37 | 'Responses/getAroundSeen': [Responses.GetAroundSeenOptions, any]; 38 | 'Responses/responseAdd': [Responses.ResponseAddOptions, any]; 39 | 'Responses/responseDelete': [Responses.ResponseDeleteOptions, any]; 40 | 'Responses/edit': [Responses.EditOptions, any]; 41 | 'Responses/reportAbuse': [Responses.ReportAbuseOptions, any]; 42 | 'FriendsFans/getFriendsByOffset': [FriendsFans.GetFriendsByOffsetOptions, any]; 43 | 'FriendsFans/getFansByOffset': [FriendsFans.GetFansByOffsetOptions, any]; 44 | 'FriendsFans/getFollowingByOffset': [FriendsFans.GetFollowingByOffsetOptions, any]; 45 | 'FriendsFans/becomeFriend': [FriendsFans.BecomeFriendOptions, any]; 46 | 'FriendsFans/removeAsFriend': [FriendsFans.RemoveAsFriendOptions, any]; 47 | 'FriendsFans/becomeFan': [FriendsFans.BecomeFanOptions, any]; 48 | 'FriendsFans/setFollowing': [FriendsFans.SetFollowingOptions, any]; 49 | 'FriendsFans/getCompletion': [FriendsFans.GetCompletionOptions, any]; 50 | 'FriendsFans/getFriendshipRequests': [FriendsFans.GetFriendshipRequestsOptions, any]; 51 | 'FriendsFans/setFollowingReplurk': [FriendsFans.SetFollowingReplurkOptions, any]; 52 | 'Alerts/getActive': [Alerts.GetActiveOptions, any]; 53 | 'Alerts/getUnreadCounts': [Alerts.GetUnreadCountsOptions, any]; 54 | 'Alerts/getHistory': [Alerts.GetHistoryOptions, any]; 55 | 'Alerts/addAsFan': [Alerts.AddAsFanOptions, any]; 56 | 'Alerts/addAllAsFan': [Alerts.AddAllAsFanOptions, any]; 57 | 'Alerts/addAllAsFriends': [Alerts.AddAllAsFriendsOptions, any]; 58 | 'Alerts/denyAll': [Alerts.DenyAllOptions, any]; 59 | 'Alerts/addAsFriend': [Alerts.AddAsFriendOptions, any]; 60 | 'Alerts/denyFriendship': [Alerts.DenyFriendshipOptions, any]; 61 | 'Alerts/removeNotification': [Alerts.RemoveNotificationOptions, any]; 62 | 'PlurkSearch/search': [PlurkSearch.SearchOptions, any]; 63 | 'UserSearch/search': [UserSearch.SearchOptions, any]; 64 | 'UserSearch/searchAllField': [UserSearch.SearchAllFieldOptions, any]; 65 | 'Emoticons/get': [Emoticons.GetOptions, any]; 66 | 'Emoticons/addFromURL': [Emoticons.AddFromURLOptions, any]; 67 | 'Emoticons/delete': [Emoticons.DeleteOptions, any]; 68 | 'Blocks/get': [Blocks.GetOptions, any]; 69 | 'Blocks/block': [Blocks.BlockOptions, any]; 70 | 'Blocks/unblock': [Blocks.UnblockOptions, any]; 71 | 'Cliques/getCliques': [Cliques.GetCliquesOptions, any]; 72 | 'Cliques/getClique': [Cliques.GetCliqueOptions, any]; 73 | 'Cliques/createClique': [Cliques.CreateCliqueOptions, any]; 74 | 'Cliques/deleteClique': [Cliques.DeleteCliqueOptions, any]; 75 | 'Cliques/renameClique': [Cliques.RenameCliqueOptions, any]; 76 | 'Cliques/add': [Cliques.AddOptions, any]; 77 | 'Cliques/remove': [Cliques.RemoveOptions, any]; 78 | 'Bookmarks/setBookmark': [Bookmarks.SetBookmarkOptions, any]; 79 | 'Bookmarks/getBookmarks': [Bookmarks.GetBookmarksOptions, any]; 80 | 'Bookmarks/getBookmark': [Bookmarks.GetBookmarkOptions, any]; 81 | 'Bookmarks/updateBookmark': [Bookmarks.UpdateBookmarkOptions, any]; 82 | 'Bookmarks/getTags': [Bookmarks.GetTagsOptions, any]; 83 | 'Bookmarks/createTag': [Bookmarks.CreateTagOptions, any]; 84 | 'Bookmarks/updateTag': [Bookmarks.UpdateTagOptions, any]; 85 | 'Bookmarks/removeTag': [Bookmarks.RemoveTagOptions, any]; 86 | 'Realtime/getUserChannel': [Realtime.GetUserChannelOptions, any]; 87 | 'checkToken': [CheckTokenOptions, any]; 88 | 'expireToken': [ExpireTokenOptions, any]; 89 | 'checkTime': [CheckTimeOptions, any]; 90 | 'checkIP': [CheckIPOptions, any]; 91 | 'echo': [EchoOptions, any]; 92 | } 93 | export declare namespace Users { 94 | interface MeOptions { 95 | } 96 | interface GetKarmaStatsOptions { 97 | } 98 | interface UpdateOptions { 99 | full_name?: any; 100 | display_name?: any; 101 | gender?: any; 102 | name_color?: any; 103 | date_of_birth?: any; 104 | birthday_privacy?: any; 105 | country_id?: any; 106 | relationship?: any; 107 | about?: any; 108 | email?: any; 109 | privacy?: any; 110 | creature?: any; 111 | creature_special?: any; 112 | filter_porn?: 0 | 1 | 2; 113 | filter_anonymous?: 0 | 1; 114 | filter_keywords?: any; 115 | pinned_plurk_id?: 0 | number; 116 | friend_list_privacy?: 'public' | 'friends-only' | 'only-me'; 117 | accept_gift?: 'always' | 'friends-only' | 'never'; 118 | } 119 | interface UpdateBackgroundOptions { 120 | bg_image: any; 121 | } 122 | interface SetNameColorOptions { 123 | color: 'red' | 'green' | 'blue' | 'default' | 'pink' | 'gold' | 'lightblue' | 'lightgreen' | 'orange' | 'purple'; 124 | } 125 | interface GetAliasesOptions { 126 | } 127 | interface SetAliasOptions { 128 | user_id: any; 129 | alias: any; 130 | } 131 | } 132 | export declare namespace Profile { 133 | interface GetOwnProfileOptions { 134 | minimal_data?: boolean; 135 | minimal_user?: boolean; 136 | include_plurks?: boolean; 137 | } 138 | interface GetPublicProfileOptions { 139 | user_id: any; 140 | nick_name: any; 141 | minimal_data?: boolean; 142 | include_plurks?: boolean; 143 | } 144 | } 145 | export declare namespace Polling { 146 | interface GetPlurksOptions { 147 | offset: any; 148 | limit?: number; 149 | favorers_detail: any; 150 | limited_detail: any; 151 | replurkers_detail: any; 152 | minimal_data?: boolean; 153 | minimal_user?: boolean; 154 | } 155 | interface GetUnreadCountOptions { 156 | } 157 | } 158 | export declare namespace Timeline { 159 | interface GetPlurkOptions { 160 | plurk_id: any; 161 | favorers_detail: any; 162 | limited_detail: any; 163 | replurkers_detail: any; 164 | minimal_data?: boolean; 165 | minimal_user?: boolean; 166 | } 167 | interface GetPlurkCountsInfoOptions { 168 | plurk_id: any; 169 | } 170 | interface GetPlurksOptions { 171 | offset: any; 172 | limit?: number; 173 | filter: any; 174 | favorers_detail: any; 175 | limited_detail: any; 176 | replurkers_detail: any; 177 | minimal_data?: boolean; 178 | minimal_user?: boolean; 179 | } 180 | interface GetPublicPlurksOptions { 181 | user_id: any; 182 | nick_name: any; 183 | offset?: string; 184 | limit?: number; 185 | favorers_detail: any; 186 | limited_detail: any; 187 | replurkers_detail: any; 188 | minimal_data?: boolean; 189 | minimal_user?: boolean; 190 | only_user?: boolean; 191 | } 192 | interface GetUnreadPlurksOptions { 193 | offset: any; 194 | limit: any; 195 | filter?: string; 196 | favorers_detail: any; 197 | limited_detail: any; 198 | replurkers_detail: any; 199 | minimal_data?: boolean; 200 | minimal_user?: boolean; 201 | } 202 | interface PlurkAddOptions { 203 | content: any; 204 | qualifier: any; 205 | limited_to?: any[]; 206 | excluded?: any; 207 | no_comments?: number; 208 | lang?: string; 209 | replurkable?: number; 210 | porn?: number; 211 | publish_to_followers?: number; 212 | publish_to_anonymous?: number; 213 | } 214 | interface PlurkDeleteOptions { 215 | plurk_id: any; 216 | } 217 | interface PlurkEditOptions { 218 | plurk_id: any; 219 | content?: any; 220 | no_comments?: null | 0 | 1 | 2; 221 | limited_to?: any; 222 | excluded?: any; 223 | replurkable?: null | true | false; 224 | porn?: any; 225 | } 226 | interface MutePlurksOptions { 227 | ids: any; 228 | } 229 | interface UnmutePlurksOptions { 230 | ids: any; 231 | } 232 | interface FavoritePlurksOptions { 233 | ids: any; 234 | } 235 | interface UnfavoritePlurksOptions { 236 | ids: any; 237 | } 238 | interface ReplurkOptions { 239 | ids: any; 240 | } 241 | interface UnreplurkOptions { 242 | ids: any; 243 | } 244 | interface MarkAsReadOptions { 245 | ids: any; 246 | note_position?: boolean; 247 | } 248 | interface UploadPictureOptions { 249 | image: any; 250 | } 251 | interface ToggleCommentsOptions { 252 | plurk_id: any; 253 | no_comments: any; 254 | } 255 | interface SetPornOptions { 256 | plurk_id: any; 257 | porn: any; 258 | } 259 | interface ReportAbuseOptions { 260 | plurk_id: any; 261 | category: any; 262 | reason: any; 263 | } 264 | interface SetUnreadSnapshotOptions { 265 | filter?: string; 266 | } 267 | interface MarkAllAsReadOptions { 268 | filter?: string; 269 | exclude_ids?: any[]; 270 | } 271 | } 272 | export declare namespace Responses { 273 | interface GetOptions { 274 | plurk_id: any; 275 | from_response?: number; 276 | minimal_data?: boolean; 277 | minimal_user?: boolean; 278 | count?: number; 279 | only_owner?: boolean; 280 | } 281 | interface GetByIdOptions { 282 | plurk_id: any; 283 | from_response_id?: number; 284 | minimal_data?: boolean; 285 | minimal_user?: boolean; 286 | count?: number; 287 | } 288 | interface GetAroundSeenOptions { 289 | plurk_id: any; 290 | minimal_data?: boolean; 291 | minimal_user?: boolean; 292 | count?: number; 293 | } 294 | interface ResponseAddOptions { 295 | plurk_id: any; 296 | content: any; 297 | qualifier: any; 298 | } 299 | interface ResponseDeleteOptions { 300 | response_id: any; 301 | plurk_id: any; 302 | } 303 | interface EditOptions { 304 | plurk_id: any; 305 | response_id: any; 306 | content: any; 307 | } 308 | interface ReportAbuseOptions { 309 | plurk_id: any; 310 | response_id: any; 311 | category: any; 312 | reason: any; 313 | } 314 | } 315 | export declare namespace FriendsFans { 316 | interface GetFriendsByOffsetOptions { 317 | user_id: any; 318 | offset?: number; 319 | limit?: number; 320 | minimal_data?: boolean; 321 | } 322 | interface GetFansByOffsetOptions { 323 | user_id: any; 324 | offset?: number; 325 | limit?: number; 326 | minimal_data?: boolean; 327 | } 328 | interface GetFollowingByOffsetOptions { 329 | offset?: number; 330 | limit?: number; 331 | minimal_data?: boolean; 332 | } 333 | interface BecomeFriendOptions { 334 | friend_id: any; 335 | } 336 | interface RemoveAsFriendOptions { 337 | friend_id: any; 338 | } 339 | interface BecomeFanOptions { 340 | fan_id: any; 341 | follow?: boolean; 342 | } 343 | interface SetFollowingOptions { 344 | user_id: any; 345 | follow: any; 346 | } 347 | interface GetCompletionOptions { 348 | } 349 | interface GetFriendshipRequestsOptions { 350 | } 351 | interface SetFollowingReplurkOptions { 352 | user_id: any; 353 | follow: any; 354 | } 355 | } 356 | export declare namespace Alerts { 357 | interface GetActiveOptions { 358 | } 359 | interface GetUnreadCountsOptions { 360 | } 361 | interface GetHistoryOptions { 362 | } 363 | interface AddAsFanOptions { 364 | user_id: any; 365 | } 366 | interface AddAllAsFanOptions { 367 | } 368 | interface AddAllAsFriendsOptions { 369 | } 370 | interface DenyAllOptions { 371 | } 372 | interface AddAsFriendOptions { 373 | user_id: any; 374 | } 375 | interface DenyFriendshipOptions { 376 | user_id: any; 377 | } 378 | interface RemoveNotificationOptions { 379 | user_id: any; 380 | } 381 | } 382 | export declare namespace PlurkSearch { 383 | interface SearchOptions { 384 | query: any; 385 | offset?: number; 386 | } 387 | } 388 | export declare namespace UserSearch { 389 | interface SearchOptions { 390 | query: any; 391 | offset?: number; 392 | type?: string; 393 | } 394 | interface SearchAllFieldOptions { 395 | query: any; 396 | offset?: number; 397 | } 398 | } 399 | export declare namespace Emoticons { 400 | interface GetOptions { 401 | custom_only?: boolean; 402 | non_custom_only?: boolean; 403 | } 404 | interface AddFromURLOptions { 405 | url: any; 406 | keyword?: any; 407 | } 408 | interface DeleteOptions { 409 | url: any; 410 | } 411 | } 412 | export declare namespace Blocks { 413 | interface GetOptions { 414 | offset?: number; 415 | } 416 | interface BlockOptions { 417 | user_id: any; 418 | } 419 | interface UnblockOptions { 420 | user_id: any; 421 | } 422 | } 423 | export declare namespace Cliques { 424 | interface GetCliquesOptions { 425 | } 426 | interface GetCliqueOptions { 427 | clique_name: any; 428 | } 429 | interface CreateCliqueOptions { 430 | clique_name: any; 431 | } 432 | interface DeleteCliqueOptions { 433 | clique_name: any; 434 | } 435 | interface RenameCliqueOptions { 436 | clique_name: any; 437 | new_name: any; 438 | } 439 | interface AddOptions { 440 | clique_name: any; 441 | user_id: any; 442 | } 443 | interface RemoveOptions { 444 | clique_name: any; 445 | user_id: any; 446 | } 447 | } 448 | export declare namespace Bookmarks { 449 | interface SetBookmarkOptions { 450 | plurk_id: any; 451 | bookmark: any; 452 | tags: any; 453 | as_reward?: number; 454 | } 455 | interface GetBookmarksOptions { 456 | tags: any; 457 | from_bookmark_id: any; 458 | limit: any; 459 | minimal_user?: boolean; 460 | minimal_data?: boolean; 461 | } 462 | interface GetBookmarkOptions { 463 | plurk_id: any; 464 | } 465 | interface UpdateBookmarkOptions { 466 | bookmark_id: any; 467 | tags: any; 468 | } 469 | interface GetTagsOptions { 470 | } 471 | interface CreateTagOptions { 472 | tag: any; 473 | } 474 | interface UpdateTagOptions { 475 | tag: any; 476 | rename: any; 477 | } 478 | interface RemoveTagOptions { 479 | tag: any; 480 | } 481 | } 482 | export declare namespace Realtime { 483 | interface GetUserChannelOptions { 484 | } 485 | } 486 | export interface CheckTokenOptions { 487 | } 488 | export interface ExpireTokenOptions { 489 | } 490 | export interface CheckTimeOptions { 491 | } 492 | export interface CheckIPOptions { 493 | } 494 | export interface EchoOptions { 495 | data: any; 496 | } 497 | -------------------------------------------------------------------------------- /src/api-parameters.ts: -------------------------------------------------------------------------------- 1 | // Generate using plurk2, data from https://www.plurk.com/API/2/list 2 | // Do not edit manually! 3 | 4 | export interface APIParameters { 5 | [api: string]: [any, any]; 6 | 'Users/me': [ 7 | Users.MeOptions, 8 | any, 9 | ]; 10 | 'Users/getKarmaStats': [ 11 | Users.GetKarmaStatsOptions, 12 | any, 13 | ]; 14 | 'Users/update': [ 15 | Users.UpdateOptions, 16 | any, 17 | ]; 18 | 'Users/updateBackground': [ 19 | Users.UpdateBackgroundOptions, 20 | any, 21 | ]; 22 | 'Users/setNameColor': [ 23 | Users.SetNameColorOptions, 24 | any, 25 | ]; 26 | 'Users/getAliases': [ 27 | Users.GetAliasesOptions, 28 | any, 29 | ]; 30 | 'Users/setAlias': [ 31 | Users.SetAliasOptions, 32 | any, 33 | ]; 34 | 'Profile/getOwnProfile': [ 35 | Profile.GetOwnProfileOptions, 36 | any, 37 | ]; 38 | 'Profile/getPublicProfile': [ 39 | Profile.GetPublicProfileOptions, 40 | any, 41 | ]; 42 | 'Polling/getPlurks': [ 43 | Polling.GetPlurksOptions, 44 | any, 45 | ]; 46 | 'Polling/getUnreadCount': [ 47 | Polling.GetUnreadCountOptions, 48 | any, 49 | ]; 50 | 'Timeline/getPlurk': [ 51 | Timeline.GetPlurkOptions, 52 | any, 53 | ]; 54 | 'Timeline/getPlurkCountsInfo': [ 55 | Timeline.GetPlurkCountsInfoOptions, 56 | any, 57 | ]; 58 | 'Timeline/getPlurks': [ 59 | Timeline.GetPlurksOptions, 60 | any, 61 | ]; 62 | 'Timeline/getPublicPlurks': [ 63 | Timeline.GetPublicPlurksOptions, 64 | any, 65 | ]; 66 | 'Timeline/getUnreadPlurks': [ 67 | Timeline.GetUnreadPlurksOptions, 68 | any, 69 | ]; 70 | 'Timeline/plurkAdd': [ 71 | Timeline.PlurkAddOptions, 72 | any, 73 | ]; 74 | 'Timeline/plurkDelete': [ 75 | Timeline.PlurkDeleteOptions, 76 | any, 77 | ]; 78 | 'Timeline/plurkEdit': [ 79 | Timeline.PlurkEditOptions, 80 | any, 81 | ]; 82 | 'Timeline/mutePlurks': [ 83 | Timeline.MutePlurksOptions, 84 | any, 85 | ]; 86 | 'Timeline/unmutePlurks': [ 87 | Timeline.UnmutePlurksOptions, 88 | any, 89 | ]; 90 | 'Timeline/favoritePlurks': [ 91 | Timeline.FavoritePlurksOptions, 92 | any, 93 | ]; 94 | 'Timeline/unfavoritePlurks': [ 95 | Timeline.UnfavoritePlurksOptions, 96 | any, 97 | ]; 98 | 'Timeline/replurk': [ 99 | Timeline.ReplurkOptions, 100 | any, 101 | ]; 102 | 'Timeline/unreplurk': [ 103 | Timeline.UnreplurkOptions, 104 | any, 105 | ]; 106 | 'Timeline/markAsRead': [ 107 | Timeline.MarkAsReadOptions, 108 | any, 109 | ]; 110 | 'Timeline/uploadPicture': [ 111 | Timeline.UploadPictureOptions, 112 | any, 113 | ]; 114 | 'Timeline/toggleComments': [ 115 | Timeline.ToggleCommentsOptions, 116 | any, 117 | ]; 118 | 'Timeline/setPorn': [ 119 | Timeline.SetPornOptions, 120 | any, 121 | ]; 122 | 'Timeline/reportAbuse': [ 123 | Timeline.ReportAbuseOptions, 124 | any, 125 | ]; 126 | 'Timeline/setUnreadSnapshot': [ 127 | Timeline.SetUnreadSnapshotOptions, 128 | any, 129 | ]; 130 | 'Timeline/markAllAsRead': [ 131 | Timeline.MarkAllAsReadOptions, 132 | any, 133 | ]; 134 | 'Responses/get': [ 135 | Responses.GetOptions, 136 | any, 137 | ]; 138 | 'Responses/getById': [ 139 | Responses.GetByIdOptions, 140 | any, 141 | ]; 142 | 'Responses/getAroundSeen': [ 143 | Responses.GetAroundSeenOptions, 144 | any, 145 | ]; 146 | 'Responses/responseAdd': [ 147 | Responses.ResponseAddOptions, 148 | any, 149 | ]; 150 | 'Responses/responseDelete': [ 151 | Responses.ResponseDeleteOptions, 152 | any, 153 | ]; 154 | 'Responses/edit': [ 155 | Responses.EditOptions, 156 | any, 157 | ]; 158 | 'Responses/reportAbuse': [ 159 | Responses.ReportAbuseOptions, 160 | any, 161 | ]; 162 | 'FriendsFans/getFriendsByOffset': [ 163 | FriendsFans.GetFriendsByOffsetOptions, 164 | any, 165 | ]; 166 | 'FriendsFans/getFansByOffset': [ 167 | FriendsFans.GetFansByOffsetOptions, 168 | any, 169 | ]; 170 | 'FriendsFans/getFollowingByOffset': [ 171 | FriendsFans.GetFollowingByOffsetOptions, 172 | any, 173 | ]; 174 | 'FriendsFans/becomeFriend': [ 175 | FriendsFans.BecomeFriendOptions, 176 | any, 177 | ]; 178 | 'FriendsFans/removeAsFriend': [ 179 | FriendsFans.RemoveAsFriendOptions, 180 | any, 181 | ]; 182 | 'FriendsFans/becomeFan': [ 183 | FriendsFans.BecomeFanOptions, 184 | any, 185 | ]; 186 | 'FriendsFans/setFollowing': [ 187 | FriendsFans.SetFollowingOptions, 188 | any, 189 | ]; 190 | 'FriendsFans/getCompletion': [ 191 | FriendsFans.GetCompletionOptions, 192 | any, 193 | ]; 194 | 'FriendsFans/getFriendshipRequests': [ 195 | FriendsFans.GetFriendshipRequestsOptions, 196 | any, 197 | ]; 198 | 'FriendsFans/setFollowingReplurk': [ 199 | FriendsFans.SetFollowingReplurkOptions, 200 | any, 201 | ]; 202 | 'Alerts/getActive': [ 203 | Alerts.GetActiveOptions, 204 | any, 205 | ]; 206 | 'Alerts/getUnreadCounts': [ 207 | Alerts.GetUnreadCountsOptions, 208 | any, 209 | ]; 210 | 'Alerts/getHistory': [ 211 | Alerts.GetHistoryOptions, 212 | any, 213 | ]; 214 | 'Alerts/addAsFan': [ 215 | Alerts.AddAsFanOptions, 216 | any, 217 | ]; 218 | 'Alerts/addAllAsFan': [ 219 | Alerts.AddAllAsFanOptions, 220 | any, 221 | ]; 222 | 'Alerts/addAllAsFriends': [ 223 | Alerts.AddAllAsFriendsOptions, 224 | any, 225 | ]; 226 | 'Alerts/denyAll': [ 227 | Alerts.DenyAllOptions, 228 | any, 229 | ]; 230 | 'Alerts/addAsFriend': [ 231 | Alerts.AddAsFriendOptions, 232 | any, 233 | ]; 234 | 'Alerts/denyFriendship': [ 235 | Alerts.DenyFriendshipOptions, 236 | any, 237 | ]; 238 | 'Alerts/removeNotification': [ 239 | Alerts.RemoveNotificationOptions, 240 | any, 241 | ]; 242 | 'PlurkSearch/search': [ 243 | PlurkSearch.SearchOptions, 244 | any, 245 | ]; 246 | 'UserSearch/search': [ 247 | UserSearch.SearchOptions, 248 | any, 249 | ]; 250 | 'UserSearch/searchAllField': [ 251 | UserSearch.SearchAllFieldOptions, 252 | any, 253 | ]; 254 | 'Emoticons/get': [ 255 | Emoticons.GetOptions, 256 | any, 257 | ]; 258 | 'Emoticons/addFromURL': [ 259 | Emoticons.AddFromURLOptions, 260 | any, 261 | ]; 262 | 'Emoticons/delete': [ 263 | Emoticons.DeleteOptions, 264 | any, 265 | ]; 266 | 'Blocks/get': [ 267 | Blocks.GetOptions, 268 | any, 269 | ]; 270 | 'Blocks/block': [ 271 | Blocks.BlockOptions, 272 | any, 273 | ]; 274 | 'Blocks/unblock': [ 275 | Blocks.UnblockOptions, 276 | any, 277 | ]; 278 | 'Cliques/getCliques': [ 279 | Cliques.GetCliquesOptions, 280 | any, 281 | ]; 282 | 'Cliques/getClique': [ 283 | Cliques.GetCliqueOptions, 284 | any, 285 | ]; 286 | 'Cliques/createClique': [ 287 | Cliques.CreateCliqueOptions, 288 | any, 289 | ]; 290 | 'Cliques/deleteClique': [ 291 | Cliques.DeleteCliqueOptions, 292 | any, 293 | ]; 294 | 'Cliques/renameClique': [ 295 | Cliques.RenameCliqueOptions, 296 | any, 297 | ]; 298 | 'Cliques/add': [ 299 | Cliques.AddOptions, 300 | any, 301 | ]; 302 | 'Cliques/remove': [ 303 | Cliques.RemoveOptions, 304 | any, 305 | ]; 306 | 'Bookmarks/setBookmark': [ 307 | Bookmarks.SetBookmarkOptions, 308 | any, 309 | ]; 310 | 'Bookmarks/getBookmarks': [ 311 | Bookmarks.GetBookmarksOptions, 312 | any, 313 | ]; 314 | 'Bookmarks/getBookmark': [ 315 | Bookmarks.GetBookmarkOptions, 316 | any, 317 | ]; 318 | 'Bookmarks/updateBookmark': [ 319 | Bookmarks.UpdateBookmarkOptions, 320 | any, 321 | ]; 322 | 'Bookmarks/getTags': [ 323 | Bookmarks.GetTagsOptions, 324 | any, 325 | ]; 326 | 'Bookmarks/createTag': [ 327 | Bookmarks.CreateTagOptions, 328 | any, 329 | ]; 330 | 'Bookmarks/updateTag': [ 331 | Bookmarks.UpdateTagOptions, 332 | any, 333 | ]; 334 | 'Bookmarks/removeTag': [ 335 | Bookmarks.RemoveTagOptions, 336 | any, 337 | ]; 338 | 'Realtime/getUserChannel': [ 339 | Realtime.GetUserChannelOptions, 340 | any, 341 | ]; 342 | 'checkToken': [ 343 | CheckTokenOptions, 344 | any, 345 | ]; 346 | 'expireToken': [ 347 | ExpireTokenOptions, 348 | any, 349 | ]; 350 | 'checkTime': [ 351 | CheckTimeOptions, 352 | any, 353 | ]; 354 | 'checkIP': [ 355 | CheckIPOptions, 356 | any, 357 | ]; 358 | 'echo': [ 359 | EchoOptions, 360 | any, 361 | ]; 362 | } 363 | 364 | export namespace Users { 365 | export interface MeOptions { 366 | } 367 | 368 | export interface GetKarmaStatsOptions { 369 | } 370 | 371 | export interface UpdateOptions { 372 | full_name?: any; 373 | display_name?: any; 374 | gender?: any; 375 | name_color?: any; 376 | date_of_birth?: any; 377 | birthday_privacy?: any; 378 | country_id?: any; 379 | relationship?: any; 380 | about?: any; 381 | email?: any; 382 | privacy?: any; 383 | creature?: any; 384 | creature_special?: any; 385 | filter_porn?: 0 | 1 | 2; 386 | filter_anonymous?: 0 | 1; 387 | filter_keywords?: any; 388 | pinned_plurk_id?: 0 | number; 389 | friend_list_privacy?: 'public' | 'friends-only' | 'only-me'; 390 | accept_gift?: 'always' | 'friends-only' | 'never'; 391 | } 392 | 393 | export interface UpdateBackgroundOptions { 394 | bg_image: any; 395 | } 396 | 397 | export interface SetNameColorOptions { 398 | color: 'red' | 'green' | 'blue' | 'default' | 'pink' | 'gold' | 'lightblue' | 'lightgreen' | 'orange' | 'purple'; 399 | } 400 | 401 | export interface GetAliasesOptions { 402 | } 403 | 404 | export interface SetAliasOptions { 405 | user_id: any; 406 | alias: any; 407 | } 408 | } 409 | 410 | export namespace Profile { 411 | export interface GetOwnProfileOptions { 412 | minimal_data?: boolean; 413 | minimal_user?: boolean; 414 | include_plurks?: boolean; 415 | } 416 | 417 | export interface GetPublicProfileOptions { 418 | user_id: any; 419 | nick_name: any; 420 | minimal_data?: boolean; 421 | include_plurks?: boolean; 422 | } 423 | } 424 | 425 | export namespace Polling { 426 | export interface GetPlurksOptions { 427 | offset: any; 428 | limit?: number; 429 | favorers_detail: any; 430 | limited_detail: any; 431 | replurkers_detail: any; 432 | minimal_data?: boolean; 433 | minimal_user?: boolean; 434 | } 435 | 436 | export interface GetUnreadCountOptions { 437 | } 438 | } 439 | 440 | export namespace Timeline { 441 | export interface GetPlurkOptions { 442 | plurk_id: any; 443 | favorers_detail: any; 444 | limited_detail: any; 445 | replurkers_detail: any; 446 | minimal_data?: boolean; 447 | minimal_user?: boolean; 448 | } 449 | 450 | export interface GetPlurkCountsInfoOptions { 451 | plurk_id: any; 452 | } 453 | 454 | export interface GetPlurksOptions { 455 | offset: any; 456 | limit?: number; 457 | filter: any; 458 | favorers_detail: any; 459 | limited_detail: any; 460 | replurkers_detail: any; 461 | minimal_data?: boolean; 462 | minimal_user?: boolean; 463 | } 464 | 465 | export interface GetPublicPlurksOptions { 466 | user_id: any; 467 | nick_name: any; 468 | offset?: string; 469 | limit?: number; 470 | favorers_detail: any; 471 | limited_detail: any; 472 | replurkers_detail: any; 473 | minimal_data?: boolean; 474 | minimal_user?: boolean; 475 | only_user?: boolean; 476 | } 477 | 478 | export interface GetUnreadPlurksOptions { 479 | offset: any; 480 | limit: any; 481 | filter?: string; 482 | favorers_detail: any; 483 | limited_detail: any; 484 | replurkers_detail: any; 485 | minimal_data?: boolean; 486 | minimal_user?: boolean; 487 | } 488 | 489 | export interface PlurkAddOptions { 490 | content: any; 491 | qualifier: any; 492 | limited_to?: any[]; 493 | excluded?: any; 494 | no_comments?: number; 495 | lang?: string; 496 | replurkable?: number; 497 | porn?: number; 498 | publish_to_followers?: number; 499 | publish_to_anonymous?: number; 500 | } 501 | 502 | export interface PlurkDeleteOptions { 503 | plurk_id: any; 504 | } 505 | 506 | export interface PlurkEditOptions { 507 | plurk_id: any; 508 | content?: any; 509 | no_comments?: null | 0 | 1 | 2; 510 | limited_to?: any; 511 | excluded?: any; 512 | replurkable?: null | true | false; 513 | porn?: any; 514 | } 515 | 516 | export interface MutePlurksOptions { 517 | ids: any; 518 | } 519 | 520 | export interface UnmutePlurksOptions { 521 | ids: any; 522 | } 523 | 524 | export interface FavoritePlurksOptions { 525 | ids: any; 526 | } 527 | 528 | export interface UnfavoritePlurksOptions { 529 | ids: any; 530 | } 531 | 532 | export interface ReplurkOptions { 533 | ids: any; 534 | } 535 | 536 | export interface UnreplurkOptions { 537 | ids: any; 538 | } 539 | 540 | export interface MarkAsReadOptions { 541 | ids: any; 542 | note_position?: boolean; 543 | } 544 | 545 | export interface UploadPictureOptions { 546 | image: any; 547 | } 548 | 549 | export interface ToggleCommentsOptions { 550 | plurk_id: any; 551 | no_comments: any; 552 | } 553 | 554 | export interface SetPornOptions { 555 | plurk_id: any; 556 | porn: any; 557 | } 558 | 559 | export interface ReportAbuseOptions { 560 | plurk_id: any; 561 | category: any; 562 | reason: any; 563 | } 564 | 565 | export interface SetUnreadSnapshotOptions { 566 | filter?: string; 567 | } 568 | 569 | export interface MarkAllAsReadOptions { 570 | filter?: string; 571 | exclude_ids?: any[]; 572 | } 573 | } 574 | 575 | export namespace Responses { 576 | export interface GetOptions { 577 | plurk_id: any; 578 | from_response?: number; 579 | minimal_data?: boolean; 580 | minimal_user?: boolean; 581 | count?: number; 582 | only_owner?: boolean; 583 | } 584 | 585 | export interface GetByIdOptions { 586 | plurk_id: any; 587 | from_response_id?: number; 588 | minimal_data?: boolean; 589 | minimal_user?: boolean; 590 | count?: number; 591 | } 592 | 593 | export interface GetAroundSeenOptions { 594 | plurk_id: any; 595 | minimal_data?: boolean; 596 | minimal_user?: boolean; 597 | count?: number; 598 | } 599 | 600 | export interface ResponseAddOptions { 601 | plurk_id: any; 602 | content: any; 603 | qualifier: any; 604 | } 605 | 606 | export interface ResponseDeleteOptions { 607 | response_id: any; 608 | plurk_id: any; 609 | } 610 | 611 | export interface EditOptions { 612 | plurk_id: any; 613 | response_id: any; 614 | content: any; 615 | } 616 | 617 | export interface ReportAbuseOptions { 618 | plurk_id: any; 619 | response_id: any; 620 | category: any; 621 | reason: any; 622 | } 623 | } 624 | 625 | export namespace FriendsFans { 626 | export interface GetFriendsByOffsetOptions { 627 | user_id: any; 628 | offset?: number; 629 | limit?: number; 630 | minimal_data?: boolean; 631 | } 632 | 633 | export interface GetFansByOffsetOptions { 634 | user_id: any; 635 | offset?: number; 636 | limit?: number; 637 | minimal_data?: boolean; 638 | } 639 | 640 | export interface GetFollowingByOffsetOptions { 641 | offset?: number; 642 | limit?: number; 643 | minimal_data?: boolean; 644 | } 645 | 646 | export interface BecomeFriendOptions { 647 | friend_id: any; 648 | } 649 | 650 | export interface RemoveAsFriendOptions { 651 | friend_id: any; 652 | } 653 | 654 | export interface BecomeFanOptions { 655 | fan_id: any; 656 | follow?: boolean; 657 | } 658 | 659 | export interface SetFollowingOptions { 660 | user_id: any; 661 | follow: any; 662 | } 663 | 664 | export interface GetCompletionOptions { 665 | } 666 | 667 | export interface GetFriendshipRequestsOptions { 668 | } 669 | 670 | export interface SetFollowingReplurkOptions { 671 | user_id: any; 672 | follow: any; 673 | } 674 | } 675 | 676 | export namespace Alerts { 677 | export interface GetActiveOptions { 678 | } 679 | 680 | export interface GetUnreadCountsOptions { 681 | } 682 | 683 | export interface GetHistoryOptions { 684 | } 685 | 686 | export interface AddAsFanOptions { 687 | user_id: any; 688 | } 689 | 690 | export interface AddAllAsFanOptions { 691 | } 692 | 693 | export interface AddAllAsFriendsOptions { 694 | } 695 | 696 | export interface DenyAllOptions { 697 | } 698 | 699 | export interface AddAsFriendOptions { 700 | user_id: any; 701 | } 702 | 703 | export interface DenyFriendshipOptions { 704 | user_id: any; 705 | } 706 | 707 | export interface RemoveNotificationOptions { 708 | user_id: any; 709 | } 710 | } 711 | 712 | export namespace PlurkSearch { 713 | export interface SearchOptions { 714 | query: any; 715 | offset?: number; 716 | } 717 | } 718 | 719 | export namespace UserSearch { 720 | export interface SearchOptions { 721 | query: any; 722 | offset?: number; 723 | type?: string; 724 | } 725 | 726 | export interface SearchAllFieldOptions { 727 | query: any; 728 | offset?: number; 729 | } 730 | } 731 | 732 | export namespace Emoticons { 733 | export interface GetOptions { 734 | custom_only?: boolean; 735 | non_custom_only?: boolean; 736 | } 737 | 738 | export interface AddFromURLOptions { 739 | url: any; 740 | keyword?: any; 741 | } 742 | 743 | export interface DeleteOptions { 744 | url: any; 745 | } 746 | } 747 | 748 | export namespace Blocks { 749 | export interface GetOptions { 750 | offset?: number; 751 | } 752 | 753 | export interface BlockOptions { 754 | user_id: any; 755 | } 756 | 757 | export interface UnblockOptions { 758 | user_id: any; 759 | } 760 | } 761 | 762 | export namespace Cliques { 763 | export interface GetCliquesOptions { 764 | } 765 | 766 | export interface GetCliqueOptions { 767 | clique_name: any; 768 | } 769 | 770 | export interface CreateCliqueOptions { 771 | clique_name: any; 772 | } 773 | 774 | export interface DeleteCliqueOptions { 775 | clique_name: any; 776 | } 777 | 778 | export interface RenameCliqueOptions { 779 | clique_name: any; 780 | new_name: any; 781 | } 782 | 783 | export interface AddOptions { 784 | clique_name: any; 785 | user_id: any; 786 | } 787 | 788 | export interface RemoveOptions { 789 | clique_name: any; 790 | user_id: any; 791 | } 792 | } 793 | 794 | export namespace Bookmarks { 795 | export interface SetBookmarkOptions { 796 | plurk_id: any; 797 | bookmark: any; 798 | tags: any; 799 | as_reward?: number; 800 | } 801 | 802 | export interface GetBookmarksOptions { 803 | tags: any; 804 | from_bookmark_id: any; 805 | limit: any; 806 | minimal_user?: boolean; 807 | minimal_data?: boolean; 808 | } 809 | 810 | export interface GetBookmarkOptions { 811 | plurk_id: any; 812 | } 813 | 814 | export interface UpdateBookmarkOptions { 815 | bookmark_id: any; 816 | tags: any; 817 | } 818 | 819 | export interface GetTagsOptions { 820 | } 821 | 822 | export interface CreateTagOptions { 823 | tag: any; 824 | } 825 | 826 | export interface UpdateTagOptions { 827 | tag: any; 828 | rename: any; 829 | } 830 | 831 | export interface RemoveTagOptions { 832 | tag: any; 833 | } 834 | } 835 | 836 | export namespace Realtime { 837 | export interface GetUserChannelOptions { 838 | } 839 | } 840 | 841 | export interface CheckTokenOptions { 842 | } 843 | 844 | export interface ExpireTokenOptions { 845 | } 846 | 847 | export interface CheckTimeOptions { 848 | } 849 | 850 | export interface CheckIPOptions { 851 | } 852 | 853 | export interface EchoOptions { 854 | data: any; 855 | } 856 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var __extends = (this && this.__extends) || (function () { 3 | var extendStatics = function (d, b) { 4 | extendStatics = Object.setPrototypeOf || 5 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 6 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 7 | return extendStatics(d, b); 8 | }; 9 | return function (d, b) { 10 | extendStatics(d, b); 11 | function __() { this.constructor = d; } 12 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 13 | }; 14 | })(); 15 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 16 | if (k2 === undefined) k2 = k; 17 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); 18 | }) : (function(o, m, k, k2) { 19 | if (k2 === undefined) k2 = k; 20 | o[k2] = m[k]; 21 | })); 22 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 23 | for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __generator = (this && this.__generator) || function (thisArg, body) { 35 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 36 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 37 | function verb(n) { return function (v) { return step([n, v]); }; } 38 | function step(op) { 39 | if (f) throw new TypeError("Generator is already executing."); 40 | while (_) try { 41 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 42 | if (y = 0, t) op = [op[0] & 2, t.value]; 43 | switch (op[0]) { 44 | case 0: case 1: t = op; break; 45 | case 4: _.label++; return { value: op[1], done: false }; 46 | case 5: _.label++; y = op[1]; op = [0]; continue; 47 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 48 | default: 49 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 50 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 51 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 52 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 53 | if (t[2]) _.ops.pop(); 54 | _.trys.pop(); continue; 55 | } 56 | op = body.call(thisArg, _); 57 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 58 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 59 | } 60 | }; 61 | var __values = (this && this.__values) || function(o) { 62 | var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; 63 | if (m) return m.call(o); 64 | if (o && typeof o.length === "number") return { 65 | next: function () { 66 | if (o && i >= o.length) o = void 0; 67 | return { value: o && o[i++], done: !o }; 68 | } 69 | }; 70 | throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); 71 | }; 72 | Object.defineProperty(exports, "__esModule", { value: true }); 73 | exports.limitTo = exports.PlurkClient = void 0; 74 | var url_1 = require("url"); 75 | var querystring_1 = require("querystring"); 76 | var events_1 = require("events"); 77 | var request = require("request-promise"); 78 | var limit_to_1 = require("./limit-to"); 79 | Object.defineProperty(exports, "limitTo", { enumerable: true, get: function () { return limit_to_1.limitTo; } }); 80 | var endPoint = 'https://www.plurk.com/'; 81 | var requestTokenUrl = endPoint + "OAuth/request_token"; 82 | var accessTokenUrl = endPoint + "OAuth/access_token"; 83 | var pathMatcher = /^\/?(?:APP\/)?(.+)$/; 84 | /** 85 | * `PlurkClient` is a class that wraps all plurk API call and handles comet channel when enabled. 86 | * It inherits from Node.js's [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) class. 87 | */ 88 | var PlurkClient = /** @class */ (function (_super) { 89 | __extends(PlurkClient, _super); 90 | /** 91 | * Constructor 92 | * @param consumerKey Consumer token, can be obtain one from Plurk App console. 93 | * @param consumerSecret Consumer token secret, should be together with consumer token. 94 | * @param token Oauth access token, optional. 95 | * You may assign it here or use `getRequestToken()` and then `getAccessToken()` 96 | * to obtain one from user with oauth authentication flow. 97 | * @param tokenSecret Oauth access token secret, optional. Also this should be come with access token. 98 | */ 99 | function PlurkClient(consumerKey, consumerSecret, token, tokenSecret) { 100 | if (token === void 0) { token = ''; } 101 | if (tokenSecret === void 0) { tokenSecret = ''; } 102 | var _this = _super.call(this) || this; 103 | /** 104 | * Flag indicates if the commet channel is started. 105 | */ 106 | _this.cometStarted = false; 107 | /** 108 | * Boolean field, set to `true` to automatic stops the 109 | * comet channel when any error has been thrown, 110 | * or else it will keep reconnect even have errors. 111 | */ 112 | _this.stopCometOnError = false; 113 | /** 114 | * Boolean field, set to `true` to populate the user data 115 | * to specific fields. For example, response data in comet channel 116 | * will have a `user` field with user details in it if detail of `user_id` 117 | * is found in raw channel response. 118 | */ 119 | _this.populateUsers = false; 120 | _this.consumerKey = consumerKey; 121 | _this.consumerSecret = consumerSecret; 122 | _this.token = token; 123 | _this.tokenSecret = tokenSecret; 124 | return _this; 125 | } 126 | /** 127 | * Get oauth request token (temporary) for user to authenticate. 128 | * It will assigns `token` and `tokenSecret` of current instance for further process. 129 | * @param callback Redirect URL after authenticate success, can be omitted if this is not a web app. 130 | * @return {PromiseLike.} Current plurk client instance. 131 | */ 132 | PlurkClient.prototype.getRequestToken = function (callback) { 133 | if (callback === void 0) { callback = ''; } 134 | return __awaiter(this, void 0, void 0, function () { 135 | var body; 136 | return __generator(this, function (_a) { 137 | switch (_a.label) { 138 | case 0: return [4 /*yield*/, request({ 139 | method: 'POST', url: requestTokenUrl, 140 | oauth: this._getOAuthParams({ callback: callback }) 141 | })]; 142 | case 1: 143 | body = _a.sent(); 144 | return [2 /*return*/, this._setOAuthParams(body)]; 145 | } 146 | }); 147 | }); 148 | }; 149 | /** 150 | * Get oauth access token (permanent) for requesting other API. 151 | * It will assigns `token` and `tokenSecret` of current instance. 152 | * Should be called once users' verifier has been received. 153 | * @param verifier The oauth verifier received from the user. 154 | * @return {PromiseLike.} Current plurk client instance. 155 | */ 156 | PlurkClient.prototype.getAccessToken = function (verifier) { 157 | return __awaiter(this, void 0, void 0, function () { 158 | var body; 159 | return __generator(this, function (_a) { 160 | switch (_a.label) { 161 | case 0: return [4 /*yield*/, request({ 162 | method: 'POST', url: accessTokenUrl, 163 | oauth: this._getOAuthParams({ verifier: verifier }) 164 | })]; 165 | case 1: 166 | body = _a.sent(); 167 | return [2 /*return*/, this._setOAuthParams(body)]; 168 | } 169 | }); 170 | }); 171 | }; 172 | PlurkClient.prototype.request = function (api, parameters) { 173 | var _a; 174 | var resolved = pathMatcher.exec(api); 175 | if (!resolved || resolved.length < 2) 176 | throw new Error("Invalid api path '" + api + "'"); 177 | var form = {}; 178 | var useFormData = false; 179 | if (parameters) 180 | for (var key in parameters) { 181 | var value = parameters[key]; 182 | switch (typeof value) { 183 | case 'undefined': 184 | case 'function': 185 | case 'symbol': break; 186 | case 'object': 187 | if (value instanceof Date) 188 | form[key] = value.toISOString(); 189 | else if (value && (value instanceof Buffer || typeof value.pipe === 'function')) { 190 | form[key] = value; 191 | useFormData = true; 192 | } 193 | else 194 | form[key] = JSON.stringify(value); 195 | break; 196 | default: 197 | form[key] = value; 198 | break; 199 | } 200 | } 201 | return request((_a = { 202 | url: endPoint + "APP/" + resolved[1] 203 | }, 204 | _a[useFormData ? 'formData' : 'form'] = form, 205 | _a.method = 'POST', 206 | _a.json = true, 207 | _a.jsonReviver = PlurkClientUtils.parseResponse, 208 | _a.headers = { 209 | 'Content-Type': useFormData ? 210 | 'multipart/form-data' : 211 | 'application/x-www-form-urlencoded', 212 | }, 213 | _a.oauth = this._getOAuthParams(), 214 | _a.time = true, 215 | _a.transform = transformWithTiming, 216 | _a)); 217 | }; 218 | /** 219 | * Start long poll from comet channel, it auto handles request for comet server 220 | * URL and it will auto keep polling until you stops it. 221 | */ 222 | PlurkClient.prototype.startComet = function () { 223 | var _this = this; 224 | if (this.cometStarted) 225 | return; 226 | this.cometStarted = true; 227 | this.request('Realtime/getUserChannel') 228 | .then(function (data) { 229 | if (!data.comet_server) 230 | throw new Error('Comet URL not found'); 231 | _this._cometUrl = url_1.parse(data.comet_server, true); 232 | if (_this.cometStarted) 233 | _this.pollComet(); 234 | }) 235 | .catch(function (err) { 236 | _this.cometStarted = false; 237 | _this.emit('error', err); 238 | }); 239 | }; 240 | /** 241 | * Stops long poll from comet channel. 242 | */ 243 | PlurkClient.prototype.stopComet = function () { 244 | if (!this.cometStarted) 245 | return; 246 | this.cometStarted = false; 247 | if (this._pollCometRequest) { 248 | this._pollCometRequest.cancel(); 249 | delete this._pollCometRequest; 250 | } 251 | }; 252 | /** 253 | * Restart long poll from comet channel. 254 | * Normally this method is automatically called while polling. 255 | */ 256 | PlurkClient.prototype.pollComet = function () { 257 | var _this = this; 258 | if (this._pollCometRequest) 259 | this._pollCometRequest.cancel(); 260 | if (!this.cometStarted) 261 | return; 262 | if (!this._cometUrl) 263 | throw new Error('Unknown comet url'); 264 | this._pollCometRequest = request({ 265 | url: this._cometUrl, timeout: 60000, 266 | agentOptions: { rejectUnauthorized: false } 267 | }) 268 | .then(function (response) { 269 | var e_1, _a; 270 | var _b; 271 | if (!_this._cometUrl) 272 | throw new Error('Unknown comet url'); 273 | var parsedResponse = JSON.parse(response.substring(response.indexOf('{'), response.lastIndexOf('}') + 1), PlurkClientUtils.parseResponse); 274 | _this.emit('comet', parsedResponse, response); 275 | var data = parsedResponse.data, user = parsedResponse.user, new_offset = parsedResponse.new_offset; 276 | if (((_b = _this._cometUrl) === null || _b === void 0 ? void 0 : _b.query) && typeof _this._cometUrl.query !== 'string') 277 | _this._cometUrl.query.offset = new_offset; 278 | delete _this._cometUrl.search; 279 | if (data && data.length) 280 | try { 281 | for (var data_1 = __values(data), data_1_1 = data_1.next(); !data_1_1.done; data_1_1 = data_1.next()) { 282 | var entry = data_1_1.value; 283 | if (_this.populateUsers && user) 284 | PlurkClientUtils.populateUsers(entry, user); 285 | if (entry && entry.type) 286 | _this.emit(entry.type, entry); 287 | } 288 | } 289 | catch (e_1_1) { e_1 = { error: e_1_1 }; } 290 | finally { 291 | try { 292 | if (data_1_1 && !data_1_1.done && (_a = data_1.return)) _a.call(data_1); 293 | } 294 | finally { if (e_1) throw e_1.error; } 295 | } 296 | process.nextTick(PlurkClientUtils.pollComet, _this); 297 | }) 298 | .catch(function (err) { 299 | if (_this.stopCometOnError) 300 | _this.cometStarted = false; 301 | else 302 | process.nextTick(PlurkClientUtils.pollComet, _this); 303 | _this.emit('error', err); 304 | }) 305 | .finally(function () { 306 | delete _this._pollCometRequest; 307 | }); 308 | }; 309 | Object.defineProperty(PlurkClient.prototype, "authPage", { 310 | /** 311 | * User authentication URL. Should be inform user to navigate to this URL 312 | * once the promise of `getRequestToken(...)` has been resolved. 313 | */ 314 | get: function () { 315 | return this.token ? endPoint + "OAuth/authorize?oauth_token=" + this.token : ''; 316 | }, 317 | enumerable: false, 318 | configurable: true 319 | }); 320 | Object.defineProperty(PlurkClient.prototype, "mobileAuthPage", { 321 | /** 322 | * Mobile version of user authentication URL. 323 | * Users may navigate to this URL instead of `authPage` if they are using smartphones. 324 | */ 325 | get: function () { 326 | return this.token ? endPoint + "m/authorize?oauth_token=" + this.token : ''; 327 | }, 328 | enumerable: false, 329 | configurable: true 330 | }); 331 | PlurkClient.prototype._getOAuthParams = function (params) { 332 | if (params === void 0) { params = {}; } 333 | params.consumer_key = this.consumerKey; 334 | params.consumer_secret = this.consumerSecret; 335 | if (this.token) 336 | params.token = this.token; 337 | if (this.tokenSecret) 338 | params.token_secret = this.tokenSecret; 339 | return params; 340 | }; 341 | PlurkClient.prototype._setOAuthParams = function (body) { 342 | var val = querystring_1.parse(body); 343 | if (val.oauth_token) 344 | this.token = val.oauth_token; 345 | if (val.oauth_token_secret) 346 | this.tokenSecret = val.oauth_token_secret; 347 | return this; 348 | }; 349 | return PlurkClient; 350 | }(events_1.EventEmitter)); 351 | exports.PlurkClient = PlurkClient; 352 | function transformWithTiming(body, response, resolveFullResponse) { 353 | if (!resolveFullResponse) { 354 | assignIfExists(body, response, 'elapsedTime'); 355 | assignIfExists(body, response, 'responseStartTime'); 356 | assignIfExists(body, response, 'timingStart'); 357 | assignIfExists(body, response, 'timings'); 358 | assignIfExists(body, response, 'timingPhases'); 359 | return body; 360 | } 361 | return response; 362 | } 363 | function assignIfExists(a, b, key) { 364 | if ((key in b) && !(key in a)) 365 | a[key] = b[key]; 366 | } 367 | var PlurkClientUtils; 368 | (function (PlurkClientUtils) { 369 | var plurkLimitToMatcher = /^(?:\|[0-9]+\|)*$/; 370 | function pollComet(client) { 371 | return client.pollComet(); 372 | } 373 | PlurkClientUtils.pollComet = pollComet; 374 | function parseResponse(key, value) { 375 | switch (key) { 376 | case 'limited_to': 377 | if (typeof value === 'string' && 378 | plurkLimitToMatcher.test(value)) 379 | return limit_to_1.limitTo.parse(value); 380 | break; 381 | case 'date_of_birth': 382 | case 'posted': 383 | case 'now': 384 | case 'issued': 385 | if (typeof value === 'string') 386 | return new Date(value); 387 | break; 388 | case 'timestamp': 389 | if (typeof value === 'number') 390 | return new Date(value * 1000); 391 | break; 392 | } 393 | return value; 394 | } 395 | PlurkClientUtils.parseResponse = parseResponse; 396 | function populateUsers(plurkData, users) { 397 | plurkData.owner = users[plurkData.owner_id]; 398 | plurkData.user = users[plurkData.user_id]; 399 | plurkData.replurker = users[plurkData.replurker_id]; 400 | if (Array.isArray(plurkData.limit_to)) 401 | plurkData.limit_to_data = plurkData.limit_to.map(populateUsersEntry, users); 402 | if (Array.isArray(plurkData.favorers)) 403 | plurkData.favorers_data = plurkData.favorers.map(populateUsersEntry, users); 404 | if (Array.isArray(plurkData.replurkers)) 405 | plurkData.replurkers_data = plurkData.replurkers.map(populateUsersEntry, users); 406 | } 407 | PlurkClientUtils.populateUsers = populateUsers; 408 | function populateUsersEntry(entry) { 409 | return this[entry]; 410 | } 411 | PlurkClientUtils.populateUsersEntry = populateUsersEntry; 412 | })(PlurkClientUtils || (PlurkClientUtils = {})); 413 | __exportStar(require("./urlwatch"), exports); 414 | __exportStar(require("./base36"), exports); 415 | --------------------------------------------------------------------------------