Coverage

79%
1453
1158
295

tapi.js

77%
109
84
25
LineHitsSource
1/*!
2 * node-weibo - lib/tapi.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var utils = require('./utils');
141var TSinaAPI = require('./tsina');
151var TQQAPI = require('./tqq');
161var WeiboAPI = require('./weibo');
171var GithubAPI = require('./github');
18
191var TAPI = module.exports = {
20 TYPES: {
21 weibo: WeiboAPI, // api v2.0
22 github: GithubAPI,
23 tsina: TSinaAPI, // api v1.0
24 // twitter: TwitterAPI,
25 tqq: TQQAPI,
26 // tsohu: TSOHUAPI
27 },
28
29 enables: {},
30
31 /**
32 * Init API options, must init before use it.
33 *
34 * @param {String} blogtype, blog api type, e.g.: 'weibo', 'tqq', 'github' and so on.
35 * @param {String} appkey
36 * @param {String} secret
37 * @param {String|Object} [oauth_callback] or [oauth_options]
38 * - {String} [oauth_callback], oauth callback redirect uri.
39 * - {String} [oauth_scope], comma separated list of scopes. e.g.: `status, user`
40 * @return {[type]} [description]
41 */
42 init: function (blogtype, appkey, secret, oauth_options) {
433 if (!appkey) {
440 throw new TypeError('appkey must be set');
45 }
463 if (!secret) {
470 throw new TypeError('secret must be set');
48 }
493 if (typeof oauth_options === 'string') {
503 oauth_options = {
51 oauth_callback: oauth_options
52 };
53 }
543 var TypeAPI = this.TYPES[blogtype];
553 if (!TypeAPI) {
560 throw new TypeError(blogtype + ' api not exists');
57 }
583 var options = {
59 appkey: appkey,
60 secret: secret
61 };
623 options = utils.extend(options, oauth_options);
633 var instance = new TypeAPI(options);
643 this.enables[blogtype] = instance;
65 },
66
67 /**
68 * Auto detech which API instance to use by user.
69 *
70 * @param {User} user
71 * @return {API} api instance
72 */
73 api_dispatch: function (user) {
74115 var apiType = user.blogtype || user.blogType;
75115 return this.enables[apiType];
76 },
77
78 /**
79 * Get api instance config by user
80 *
81 * @param {User} user
82 * @return {Object} config
83 */
84 get_config: function (user) {
8513 return this.api_dispatch(user).config;
86 },
87
88 /**
89 * Check api support the method or not.
90 *
91 * @param {User} user
92 * @param {String} method
93 * @return {Boolean} true or false
94 */
95 support: function (user, method) {
9613 return this.get_config(user)['support_' + method] !== false;
97 },
98
99 /**
100 * Process text to display format.
101 *
102 * @param {User} user
103 * @param {Status} status
104 * @return {String}
105 */
106 process_text: function (user, status) {
1076 return this.api_dispatch(user).process_text(status);
108 },
109
110 /**
111 * Utils methods
112 */
113
114 _timeline: function (method, user, cursor, callback) {
11532 if (typeof cursor === 'function') {
11615 callback = cursor;
11715 cursor = null;
118 }
11932 cursor = cursor || {};
12032 cursor.count = cursor.count || 20;
12132 var max_id = cursor.max_id;
12232 var self = this;
12332 return self.api_dispatch(user)[method](user, cursor, function (err, result) {
12432 if (err || !max_id) {
12532 return callback(err, result);
126 }
1270 max_id = String(max_id);
128 // ignore the max_id status
1290 var needs = [];
1300 var statuses = result.items || [];
1310 for (var i = 0, l = statuses.length; i < l; i++) {
1320 var status = statuses[i];
1330 if (status.id === max_id) {
1340 continue;
135 }
1360 needs.push(status);
137 }
1380 result.items = needs;
1390 callback(null, result);
140 });
141 },
142
143 /**
144 * Status
145 */
146
147 /**
148 * Post a status
149 *
150 * @param {User} user, oauth user.
151 * @param {String|Object} status
152 * - {String} status, content text.
153 * - {Number} [lat], latitude.
154 * - {Number} [long], longitude.
155 * - {String} [annotations], addtional information.
156 * @param {Function(Error, Status)} callback
157 * @return {Context} this
158 */
159 update: function (user, status, callback) {
1608 if (typeof status === 'string') {
1616 status = {status: status};
162 }
1638 return this.api_dispatch(user).update(user, status, callback);
164 },
165
166 /**
167 * Post a status contain an image.
168 *
169 * @param {User} user, oauth user.
170 * @param {String|Object} status
171 * - {String} status, content text.
172 * - {Number} [lat], latitude.
173 * - {Number} [long], longitude.
174 * - {String} [annotations], addtional information.
175 * @param {Object} pic
176 * - {Buffer|ReadStream} data
177 * - {String} [name], image file name
178 * - {String} [content_type], data content type
179 * @param {Function(Error, Status)} callback
180 * @return {Context} this
181 */
182 upload: function (user, status, pic, callback) {
1836 if (typeof status === 'string') {
1844 status = {status: status};
185 }
1866 return this.api_dispatch(user).upload(user, status, pic, callback);
187 },
188
189 /**
190 * Repost a status.
191 *
192 * @param {User} user
193 * @param {String|Number} id, need to repost status id.
194 * @param {String|Object} status
195 * - {String} status, content text
196 * - {Number} [lat], latitude.
197 * - {Number} [long], longitude.
198 * - {Boolean} isComment, is comment or not, default is `false`.
199 * @param {Function(Error, Status)} callback
200 * @return {Context} this
201 */
202 repost: function (user, id, status, callback) {
2034 if (typeof status === 'string') {
2040 status = {status: status};
205 }
2064 id = String(id);
2074 return this.api_dispatch(user).repost(user, id, status, callback);
208 },
209
210 /**
211 * Remove a status by id.
212 *
213 * @param {User} user
214 * @param {String|Number} id
215 * @param {Function(Error, Status)} callback
216 * @return {Context} this
217 */
218 destroy: function (user, id, callback) {
2196 id = String(id);
2206 return this.api_dispatch(user).destroy(user, id, callback);
221 },
222
223 // upload_pic_url: function (data, pic, callback, context) {
224 // return this.api_dispatch(data).upload_pic_url(data, pic, callback, context);
225 // },
226
227 // // id 瑞推
228 // retweet: function (data, callback, context) {
229 // return this.api_dispatch(data).retweet(data, callback, context);
230 // },
231
232 /**
233 * Get a status by id.
234 *
235 * @param {User} user
236 * @param {String|Number} id
237 * @param {Function(Error, Status)} callback
238 * @return {Context} this
239 */
240 show: function (user, id, callback) {
2412 return this.api_dispatch(user).show(user, String(id), callback);
242 },
243
244 /**
245 * Get statuses comment count and repost count by ids.
246 *
247 * @param {User} user
248 * @param {String|Array} ids, separate by comma.
249 * @param {Function(err, counts)} callback
250 * - {String} id
251 * - {Number} comments
252 * - {Number} reposts
253 * @return {Context} this
254 */
255 count: function (user, ids, callback) {
2564 if (Array.isArray(ids)) {
2570 ids = ids.join(',');
258 }
2594 return this.api_dispatch(user).count(user, ids, callback);
260 },
261
262 /**
263 * List home timeline statuses.
264 *
265 * @param {User} user
266 * @param {Cursor} [cursor]
267 * - {String} since_id
268 * - {String} max_id
269 * - {String} [since_time], only for tqq
270 * - {String} [max_time], only for tqq
271 * - {Number} count, default is `20`
272 * - {Number} page
273 * @param {Function(err, result)} callback
274 * {Object} result:
275 * - {Array} items, [Status, ...]
276 * - {Cursor} cursor
277 * - ...
278 * @return {Context} this
279 */
280 home_timeline: function (user, cursor, callback) {
2814 return this._timeline('home_timeline', user, cursor, callback);
282 },
283
284 /**
285 * List home timeline statuses.
286 *
287 * @param {User} user
288 * @param {Cursor} [cursor]
289 * - {String} since_id
290 * - {String} max_id
291 * - {String} [since_time], only for tqq
292 * - {String} [max_time], only for tqq
293 * - {Number} count, default is `20`
294 * - {Number} page
295 * @param {Function(err, result)} callback
296 * {Object} result:
297 * - {Array} items, [Status, ...]
298 * - {Cursor} cursor
299 * - ...
300 * @return {Context} this
301 */
302 public_timeline: function (user, cursor, callback) {
3034 return this._timeline('public_timeline', user, cursor, callback);
304 },
305
306 /**
307 * List user personal timeline statuses.
308 *
309 * @param {User} user
310 * @param {Cursor} [cursor]
311 * - {String} [uid], user id
312 * - {String} [screen_name], `user.screen_name`, screen_name or uid must be set at least one.
313 * - {String} [since_id]
314 * - {String} [max_id]
315 * - {String} [since_time], only for tqq
316 * - {String} [max_time], only for tqq
317 * - {Number} count, default is `20`
318 * - {Number} page
319 * @param {Function(err, result)} callback
320 * {Object} result:
321 * - {Array} items, [Status, ...]
322 * - {Cursor} cursor
323 * - ...
324 * @return {Context} this
325 */
326 user_timeline: function (user, cursor, callback) {
3274 return this._timeline('user_timeline', user, cursor, callback);
328 },
329
330 /**
331 * List @me statuses.
332 *
333 * @param {User} user
334 * @param {Cursor} [cursor]
335 * - {String} since_id
336 * - {String} max_id
337 * - {String} [since_time], only for tqq
338 * - {String} [max_time], only for tqq
339 * - {Number} count, default is `20`
340 * - {Number} page
341 * @param {Function(err, result)} callback
342 * {Object} result:
343 * - {Array} items, [Status, ...]
344 * - {Cursor} cursor
345 * - ...
346 * @return {Context} this
347 */
348 mentions: function (user, cursor, callback) {
3494 return this._timeline('mentions', user, cursor, callback);
350 },
351
352 /**
353 * List one status's reposted statuses
354 *
355 * @param {User} user
356 * @param {String} id, status's id
357 * @param {Cursor} [cursor]
358 * - {String} since_id
359 * - {String} max_id
360 * - {String} [since_time], only for tqq
361 * - {String} [max_time], only for tqq
362 * - {Number} count, default is `20`
363 * - {Number} page
364 * - {Number} [filter_by_author], only support by `weibo`;
365 * Filter statuses by author type, 0: all, 1: only I following、2: stranger, default is `0`.
366 * @param {Function(err, result)} callback
367 * {Object} result:
368 * - {Array} items, [Status, ...]
369 * - {Cursor} cursor
370 * - ...
371 * @return {Context} this
372 */
373 repost_timeline: function (user, id, cursor, callback) {
3742 if (typeof cursor === 'function') {
3750 callback = cursor;
3760 cursor = null;
377 }
3782 cursor = cursor || {};
3792 cursor.id = id;
3802 return this._timeline('repost_timeline', user, cursor, callback);
381 },
382
383 /**
384 * Search statuses by query.
385 *
386 * @param {AccessToken} user
387 * @param {String|Object} query
388 * - {String} q, query keyword
389 * - {String} [long], longitude
390 * - {String} [lat], latitude
391 * - {String} [radius], radius for longitude and latitude.
392 * @param {Cursor} [cursor]
393 * - {Number} [count], default is `20`
394 * - {Number} [page], default is the first page.
395 * @param {Function(err, result)} callback
396 * @return {Context} this
397 */
398 search: function (user, query, cursor, callback) {
3991 if (typeof query === 'string') {
4001 query = {
401 q: query
402 };
403 }
4041 if (typeof cursor === 'function') {
4051 callback = cursor;
4061 cursor = null;
407 }
4081 return this.api_dispatch(user).search(user, query, cursor, callback);
409 },
410
411 /**
412 * Favorite
413 */
414
415 /**
416 * List favorites.
417 *
418 * @param {User} user
419 * @param {Cursor} [cursor]
420 * - {String} since_id
421 * - {String} max_id
422 * - {String} [since_time], only for tqq
423 * - {String} [max_time], only for tqq
424 * - {Number} count, default is `20`
425 * - {Number} page
426 * @param {Function(err, result)} callback
427 * {Object} result:
428 * - {Array} items, [Favorite, ...]
429 * - {Cursor} cursor
430 * - ...
431 * @return {Context} this
432 */
433 favorites: function (user, cursor, callback) {
4342 return this._timeline('favorites', user, cursor, callback);
435 },
436
437 /**
438 * Show a favorite item by item id.
439 *
440 * @param {User} user
441 * @param {String} id, favorite item's id.
442 * @param {Function(err, favorite)} callback
443 * @return {Context} this
444 */
445 favorite_show: function (user, id, callback) {
4460 return this.api_dispatch(user).favorite_show(user, id, callback);
447 },
448
449 /**
450 * Add a status to favorites.
451 *
452 * @param {User} user
453 * @param {String} id, status's id.
454 * @param {Function(err, result)} callback
455 * - {Object} result
456 * - {String} id, relation item's id.
457 * - addtional infomation maybe.
458 * @return {Context} this
459 */
460 favorite_create: function (user, id, callback) {
4610 return this.api_dispatch(user).favorite_create(user, id, callback);
462 },
463
464 /**
465 * Remove the status from favorites.
466 *
467 * @param {User} user
468 * @param {String} id, the favorite item's id.
469 * @param {Function(err, result)} callback
470 * - {Object} result
471 * - {String} id, relation item's id.
472 * - addtional infomation maybe.
473 * @return {Context} this
474 */
475 favorite_destroy: function (user, id, callback) {
4760 return this.api_dispatch(user).favorite_destroy(user, id, callback);
477 },
478
479 /**
480 * Comment
481 */
482
483 /**
484 * List comments to my statues
485 *
486 * @param {User} user
487 * @param {Cursor} [cursor]
488 * - {String} since_id
489 * - {String} max_id
490 * - {String} [since_time], only for tqq
491 * - {String} [max_time], only for tqq
492 * - {Number} count, default is `20`
493 * - {Number} page
494 * @param {Function(err, result)} callback
495 * {Object} result:
496 * - {Array} items, [Comment, ...]
497 * - {Cursor} cursor
498 * - ...
499 * @return {Context} this
500 */
501 comments_timeline: function (user, cursor, callback) {
5024 return this._timeline('comments_timeline', user, cursor, callback);
503 },
504
505 /**
506 * List @me comments
507 *
508 * @param {User} user
509 * @param {Cursor} [cursor]
510 * - {String} since_id
511 * - {String} max_id
512 * - {Number} count, default is `20`
513 * - {Number} page
514 * @param {Function(err, result)} callback
515 * {Object} result:
516 * - {Array} items, [Comment, ...]
517 * - {Cursor} cursor
518 * - ...
519 * @return {Context} this
520 */
521 comments_mentions: function (user, cursor, callback) {
5222 return this._timeline('comments_mentions', user, cursor, callback);
523 },
524
525 /**
526 * List comments post by me
527 *
528 * @param {User} user
529 * @param {Cursor} [cursor]
530 * - {String} since_id
531 * - {String} max_id
532 * - {Number} count, default is `20`
533 * - {Number} page
534 * - {Number} [filter_by_source], only support by `weibo`;
535 * Filter comments by source type, 0: all, 1: come from weibo, 2: come from weiqun, default is `0`.
536 * @param {Function(err, result)} callback
537 * {Object} result:
538 * - {Array} items, [Comment, ...]
539 * - {Cursor} cursor
540 * - ...
541 * @return {Context} this
542 */
543 comments_by_me: function (user, cursor, callback) {
5442 return this._timeline('comments_by_me', user, cursor, callback);
545 },
546
547 /**
548 * List comments to me
549 *
550 * @param {User} user
551 * @param {Cursor} [cursor]
552 * - {String} [since_id]
553 * - {String} [max_id]
554 * - {Number} [count], default is `20`
555 * - {Number} [page]
556 * - {Number} [filter_by_author], only support by `weibo`;
557 * Filter comments by author type, 0: all, 1: I following, 2: stranger, default is `0`.
558 * - {Number} [filter_by_source], only support by `weibo`;
559 * Filter comments by source type, 0: all, 1: come from weibo, 2: come from weiqun, default is `0`.
560 * @param {Function(err, result)} callback
561 * {Object} result:
562 * - {Array} items, [Comment, ...]
563 * - {Cursor} cursor
564 * - ...
565 * @return {Context} this
566 */
567 comments_to_me: function (user, cursor, callback) {
5682 return this._timeline('comments_to_me', user, cursor, callback);
569 },
570
571 /**
572 * List one status's comments
573 *
574 * @param {User} user
575 * @param {String} id, status's id
576 * @param {Cursor} [cursor]
577 * - {String} since_id
578 * - {String} max_id
579 * - {String} [since_time], only for tqq
580 * - {String} [max_time], only for tqq
581 * - {Number} count, default is `20`
582 * - {Number} page
583 * - {Number} [filter_by_author], only support by `weibo`;
584 * Filter comments by author type, 0: all, 1: only I following、2: stranger, default is `0`.
585 * @param {Function(err, result)} callback
586 * {Object} result:
587 * - {Array} items, [Comment, ...]
588 * - {Cursor} cursor
589 * - ...
590 * @return {Context} this
591 */
592 comments: function (user, id, cursor, callback) {
5932 if (typeof cursor === 'function') {
5940 callback = cursor;
5950 cursor = null;
596 }
5972 cursor = cursor || {};
5982 cursor.id = id;
5992 return this._timeline('comments', user, cursor, callback);
600 },
601
602 /**
603 * post a comment to a status
604 *
605 * @param {AccessToken} user
606 * @param {String} id, status's id
607 * @param {String|Object} comment
608 * - {String} comment
609 * - {Number} [comment_ori], same comment to the original status when comment on a repost status,
610 * 0: no, 1: yes, default is `0`.
611 * @param {Function(err, result)} callback
612 * - {Object} result
613 * - {String} id, the comment id
614 * @return {Context} this
615 */
616 comment_create: function (user, id, comment, callback) {
6177 if (typeof comment === 'string') {
6187 comment = {comment: comment};
619 }
6207 return this.api_dispatch(user).comment_create(user, id, comment, callback);
621 },
622
623 /**
624 * reply to a comment
625 * @param {AccessToken} user
626 * @param {String} cid, comment's id
627 * @param {String} id, status's id
628 * @param {String|Object} comment
629 * - {String} comment
630 * - {Number} without_mention, don't auto add `'reply@username'` to comment text or not,
631 * 0: yes, 1: no, default is `0`, won't auto add.
632 * - {Number} [comment_ori], same comment to the original status when comment on a repost status,
633 * 0: no, 1: yes, default is `0`.
634 * @param {Function(err, result)} callback
635 * @return {Context} this
636 */
637 comment_reply: function (user, cid, id, comment, callback) {
6386 if (typeof comment === 'string') {
6396 comment = {comment: comment};
640 }
6416 return this.api_dispatch(user).comment_reply(user, cid, id, comment, callback);
642 },
643
644 /**
645 * remove a comment
646 * @param {AccessToken} user
647 * @param {String} cid, comment's id
648 * @param {Function(err, result)} callback
649 * @return {Context} this
650 */
651 comment_destroy: function (user, cid, callback) {
6521 return this.api_dispatch(user).comment_destroy(user, cid, callback);
653 },
654
655 /**
656 * OAuth
657 */
658
659 /**
660 * Get authorization token and login url.
661 *
662 * @param {Object} user
663 * - {String} blogtype, 'weibo' or other blog type,
664 * - {String} oauth_callback, 'login callback url' or 'oob'
665 * @param {Function(err, auth_info)} callback
666 * - {Object} auth_info
667 * - {String} auth_url: 'http://xxxx/auth?xxx',
668 * - {String} oauth_token: $oauth_token,
669 * - {String} oauth_token_secret: $oauth_token_secret
670 * @return {Context} this, blogType api.
671 */
672 get_authorization_url: function (user, callback) {
6736 return this.api_dispatch(user).get_authorization_url(user, callback);
674 },
675
676 /**
677 * Get access token.
678 *
679 * @param {Object} user
680 * - {String} blogtype
681 * - {String} oauth_token, authorization `oauth_token`
682 * - {String} oauth_verifier, authorization `oauth_verifier`
683 * - {String} oauth_token_secret, request token secret
684 * @param {Function(err, token)} callback
685 * - {Object} token
686 * - {String} oauth_token
687 * - {String} oauth_token_secret
688 * @return {Context} this
689 */
690 get_access_token: function (user, callback) {
6913 return this.api_dispatch(user).get_access_token(user, callback);
692 },
693
694 /**
695 * User
696 */
697
698 /**
699 * Get user profile infomation by access token.
700 *
701 * @param {Object} user
702 * - {String} blogtype
703 * - {String} oauth_token, access oauth token
704 * - {String} [oauth_token_secret], access oauth token secret, oauth v2 don't need this param.
705 * @param {Function(err, User)} callback
706 * @return {Context} this
707 */
708 verify_credentials: function (user, callback) {
7093 return this.api_dispatch(user).verify_credentials(user, callback);
710 },
711
712 /**
713 * Get user profile infomation by uid.
714 * @param {Object} user
715 * - {String} blogtype
716 * - {String} oauth_token, access token
717 * - {String} [oauth_token_secret], access oauth token secret, oauth v2 don't need this param.
718 * @param {String} [uid], user id
719 * @param {String} [screen_name], user screen_name
720 * uid and screen_name MUST set one.
721 * @param {Function(err, User)} callback
722 * @return {Context} this
723 */
724 user_show: function (user, uid, screen_name, callback) {
7254 if (typeof screen_name === 'function') {
7263 callback = screen_name;
7273 screen_name = null;
728 }
7294 return this.api_dispatch(user).user_show(user, uid, screen_name, callback);
730 },
731
732 rate_limit_status: function (user, callback) {
7330 return this.api_dispatch(user).rate_limit_status(user, callback);
734 },
735
736 /*
737 * id false int64/string 用户ID(int64)或者昵称(string)。该参数为一个REST风格参数。调用示例见注意事项
738 * user_id false int64 用户ID,主要是用来区分用户ID跟微博昵称。当微博昵称为数字导致和用户ID产生歧义,特别是当微博昵称和用户ID一样的时候,建议使用该参数
739 * screen_name false string 微博昵称,主要是用来区分用户UID跟微博昵称,当二者一样而产生歧义的时候,建议使用该参数
740 * id, user_id, screen_name 可以任选一个参数,在3个都不提供的情况下,系统返回当前登录用户的关注列表
741 *
742 * cursor false int 用于分页请求,请求第1页cursor传-1,在返回的结果中会得到next_cursor字段,表示下一页的cursor。next_cursor为0表示已经到记录末尾。
743 * count false int,默认20,最大200 每页返回的最大记录数,最大不能超过200,默认为20。
744 */
745 friends: function (data, callback, context) {
7460 return this.api_dispatch(data).friends(data, callback, context);
747 },
748
749 // 同friends
750 followers: function (data, callback, context) {
7510 return this.api_dispatch(data).followers(data, callback, context);
752 },
753
754};
755
7561TAPI.friends_timeline = TAPI.home_timeline;

utils.js

95%
66
63
3
LineHitsSource
1/*!
2 * node-weibo - lib/utils.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var STRING_FORMAT_REGEX = /\{\{([\w\s\.\'\"\(\),-\[\]]+)?\}\}/g;
141exports.format = function (s, values) {
1594 return s.replace(STRING_FORMAT_REGEX, function (match, key) {
1639 return values[key];
17 });
18};
19
20/**
21 * 格式化字符串
22 * eg:
23 * '{0}天有{1}个小时'.format([1, 24])
24 * or
25 * '{{day}}天有{{hour}}个小时'.format({day:1, hour:24}})
26 * @param {Object} values
27 */
281String.prototype.format = function (values) {
2984 return exports.format(this, values);
30};
31
321var b64_hmac_sha1 = require('./sha1').b64_hmac_sha1;
331var crypto = require('crypto');
34
351var querystring = {
36 parse: function (s) {
377 var qs = {};
387 if (typeof s !== 'string') {
392 return qs;
40 }
415 var pairs = s.split('&');
425 for (var i = 0, len = pairs.length; i < len; i++) {
438 var pair = pairs[i].split('=', 2);
448 if (pair.length !== 2) {
453 continue;
46 }
475 var key = pair[0].trim();
485 if (!key) {
491 continue;
50 }
514 qs[decodeURIComponent(key)] = decodeURIComponent(pair[1]);
52 }
535 return qs;
54 },
55 stringify: function (data) {
565 var pairs = [];
575 data = data || {};
585 for (var k in data) {
597 pairs.push(encodeURIComponent(k) + '=' + encodeURIComponent('' + data[k]));
60 }
615 return pairs.join('&');
62 }
63};
64
651function urljoin(url, params) {
665 if (typeof params === 'object') {
674 params = querystring.stringify(params);
68 }
695 if (!params) {
702 return url;
71 }
723 if (url.indexOf('?') < 0) {
731 url += '?';
74 } else {
752 url += '&';
76 }
773 return url + params;
78}
79
801function base64HmacSha1(baseString, key) {
8144 if (b64_hmac_sha1) {
8244 return b64_hmac_sha1(key, baseString);
83 }
840 return new crypto.Hmac().init("sha1", key).update(baseString).digest("base64");
85}
86
87// HTML 编码
88// test: hard code testing 。。。 '"!@#$%^&*()-=+ |][ {} ~` &&&&&amp; &lt; & C++ c++c + +c &amp;
891function htmlencode(str) {
900 if (!str) { return ''; }
910 return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
92}
93
941exports.extend = function (destination) {
958 for (var i = 1, len = arguments.length; i < len; i++) {
9612 var source = arguments[i];
9712 if (!source) {
981 continue;
99 }
10011 for (var property in source) {
101109 destination[property] = source[property];
102 }
103 }
1048 return destination;
105};
106
1071exports.STRING_FORMAT_REGEX = STRING_FORMAT_REGEX;
1081exports.querystring = querystring;
1091exports.base64HmacSha1 = base64HmacSha1;
1101exports.urljoin = urljoin;
1111exports.htmlencode = htmlencode;
112
1131var MIME_TYPES = {
114 'jpg': 'image/jpeg',
115 'jpeg': 'image/jpeg',
116 'gif': 'image/gif',
117 'png': 'image/png',
118 'bmp': 'image/bmp',
119};
120
1211var BIN_TYPE = 'application/octet-stream';
122
1231exports.mimeLookup = function (name, fallback) {
12419 var ext = name.replace(/.*[\.\/]/, '').toLowerCase();
12519 return MIME_TYPES[ext] || fallback || BIN_TYPE;
126};
127
128/**
129 * Escape the given string of `html`.
130 *
131 * @param {String} html
132 * @return {String}
133 */
1341exports.escape = function (html) {
1353 return String(html)
136 .replace(/&(?!\w+;)/g, '&amp;')
137 .replace(/</g, '&lt;')
138 .replace(/>/g, '&gt;')
139 .replace(/"/g, '&quot;');
140};
141
142/**
143 * Remove all html tags.
144 *
145 * @param {String} s
146 * @return {String}
147 */
1481exports.removeHTML = function (s) {
1498 return s.replace(/(<.*?>)/ig, '');
150};

sha1.js

84%
119
101
18
LineHitsSource
1/*
2 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
3 * in FIPS PUB 180-1
4 * Version 2.1a Copyright Paul Johnston 2000 - 2002.
5 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
6 * Distributed under the BSD License
7 * See http://pajhome.org.uk/crypt/md5 for details.
8 */
9
101(function () {
11
121var root = this; // window on browser
131var exports;
141var crypto;
151if (typeof module === 'undefined') {
160 root.weibo = root.weibo || {};
170 exports = root.weibo.sha1 = {};
18} else {
191 exports = module.exports;
20}
21
22/*
23 * Configurable variables. You may need to tweak these to be compatible with
24 * the server-side, but the defaults work in most cases.
25 */
261var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
271var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */
281var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
29
30/*
31 * These are the functions you'll usually want to call
32 * They take string arguments and return either hex or base-64 encoded strings
33 */
341function hex_sha1(s) {
350 return binb2hex(core_sha1(str2binb(s), s.length * chrsz));
36}
371exports.hex_sha1 = hex_sha1;
38
391function b64_sha1(s) {
400 return binb2b64(core_sha1(str2binb(s), s.length * chrsz));
41}
421exports.b64_sha1 = b64_sha1;
43
441function str_sha1(s) {
450 return binb2str(core_sha1(str2binb(s), s.length * chrsz));
46}
471exports.str_sha1 = str_sha1;
48
491function hex_hmac_sha1(key, data) {
500 return binb2hex(core_hmac_sha1(key, data));
51}
521exports.hex_hmac_sha1 = hex_hmac_sha1;
53
541function b64_hmac_sha1(key, data) {
5547 return binb2b64(core_hmac_sha1(key, data));
56}
571exports.b64_hmac_sha1 = b64_hmac_sha1;
58
591function str_hmac_sha1(key, data) {
600 return binb2str(core_hmac_sha1(key, data));
61}
621exports.str_hmac_sha1 = str_hmac_sha1;
63
64/*
65 * Perform a simple self-test to see if the VM is working
66 */
671function sha1_vm_test() {
680 return hex_sha1("abc") === "a9993e364706816aba3e25717850c26c9cd0d89d";
69}
70
71/*
72 * Calculate the SHA-1 of an array of big-endian words, and a bit length
73 */
741function core_sha1(x, len) {
75 /* append padding */
76133 x[len >> 5] |= 0x80 << (24 - len % 32);
77133 x[((len + 64 >> 9) << 4) + 15] = len;
78
79133 var w = new Array(80);
80133 var a = 1732584193;
81133 var b = -271733879;
82133 var c = -1732584194;
83133 var d = 271733878;
84133 var e = -1009589776;
85
86133 for (var i = 0; i < x.length; i += 16) {
87538 var olda = a;
88538 var oldb = b;
89538 var oldc = c;
90538 var oldd = d;
91538 var olde = e;
92
93538 for (var j = 0; j < 80; j++) {
9443040 if (j < 16) {
958608 w[j] = x[i + j];
96 }
97 else {
9834432 w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
99 }
10043040 var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
101 safe_add(safe_add(e, w[j]), sha1_kt(j)));
10243040 e = d;
10343040 d = c;
10443040 c = rol(b, 30);
10543040 b = a;
10643040 a = t;
107 }
108
109538 a = safe_add(a, olda);
110538 b = safe_add(b, oldb);
111538 c = safe_add(c, oldc);
112538 d = safe_add(d, oldd);
113538 e = safe_add(e, olde);
114 }
115133 return [ a, b, c, d, e ];
116
117}
118
119/*
120 * Perform the appropriate triplet combination function for the current
121 * iteration
122 */
1231function sha1_ft(t, b, c, d) {
12443040 if (t < 20) {
12510760 return (b & c) | ((~b) & d);
126 }
12732280 if (t < 40) {
12810760 return b ^ c ^ d;
129 }
13021520 if (t < 60) {
13110760 return (b & c) | (b & d) | (c & d);
132 }
13310760 return b ^ c ^ d;
134}
135
136/*
137 * Determine the appropriate additive constant for the current iteration
138 */
1391function sha1_kt(t) {
14043040 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
141 (t < 60) ? -1894007588 : -899497514;
142}
143
144/*
145 * Calculate the HMAC-SHA1 of a key and some data
146 */
1471function core_hmac_sha1(key, data) {
14847 var bkey = str2binb(key);
14947 if (bkey.length > 16) {
15039 bkey = core_sha1(bkey, key.length * chrsz);
151 }
152
15347 var ipad = new Array(16), opad = new Array(16);
15447 for(var i = 0; i < 16; i++) {
155752 ipad[i] = bkey[i] ^ 0x36363636;
156752 opad[i] = bkey[i] ^ 0x5C5C5C5C;
157 }
158
15947 var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
16047 return core_sha1(opad.concat(hash), 512 + 160);
161}
162
163/*
164 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
165 * to work around bugs in some JS interpreters.
166 */
1671function safe_add(x, y) {
168174850 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
169174850 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
170174850 return (msw << 16) | (lsw & 0xFFFF);
171}
172
173/*
174 * Bitwise rotate a 32-bit number to the left.
175 */
1761function rol(num, cnt) {
177120512 return (num << cnt) | (num >>> (32 - cnt));
178}
179
180/*
181 * Convert an 8-bit or 16-bit string to an array of big-endian words
182 * In 8-bit function, characters >255 have their hi-byte silently ignored.
183 */
1841function str2binb(str) {
18594 var bin = Array();
18694 var mask = (1 << chrsz) - 1;
18794 for(var i = 0; i < str.length * chrsz; i += chrsz)
18821403 bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
18994 return bin;
190}
1911exports.str2binb = str2binb;
192
193/*
194 * Convert an array of big-endian words to a string
195 */
1961function binb2str(bin) {
1970 var str = "";
1980 var mask = (1 << chrsz) - 1;
1990 for(var i = 0; i < bin.length * 32; i += chrsz)
2000 str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
2010 return str;
202}
2031exports.binb2str = binb2str;
204
205/*
206 * Convert an array of big-endian words to a hex string.
207 */
2081function binb2hex(binarray) {
2090 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
2100 var str = "";
2110 for(var i = 0; i < binarray.length * 4; i++) {
2120 str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
213 hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
214 }
2150 return str;
216}
2171exports.binb2hex = binb2hex;
218
219/*
220 * Convert an array of big-endian words to a base-64 string
221 */
2221function binb2b64(binarray) {
22347 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
22447 var str = "";
22547 for(var i = 0; i < binarray.length * 4; i += 3) {
226329 var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
227 | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
228 | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
229329 for(var j = 0; j < 4; j++)
230 {
2311363 if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
2321269 else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
233 }
234 }
23547 return str;
236}
2371exports.binb2b64 = binb2b64;
238
239})();

tsina.js

36%
52
19
33
LineHitsSource
1/*!
2 * node-weibo - lib/tsina.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var inherits = require('util').inherits;
141var utils = require('./utils');
151var EventProxy = require('eventproxy').EventProxy;
161var fs = require('fs');
171var path = require('path');
181var TBase = require('./tbase');
191var weiboutil = require('./weibo_util');
20
21
221function TSinaAPI(options) {
230 TSinaAPI.super_.call(this);
24
250 var config = utils.extend({}, options, {
26 host: 'http://api.t.sina.com.cn'
27 });
280 this.init(config);
29}
30
311inherits(TSinaAPI, TBase);
321module.exports = TSinaAPI;
33
341TSinaAPI.prototype.format_authorization_url = function (params) {
350 params.forcelogin = 'true';
360 return TSinaAPI.super_.prototype.format_authorization_url.call(this, params);
37};
38
391TSinaAPI.prototype.get_result_items = function (data, playload, args) {
400 return data.results || data.users || data;
41};
42
431TSinaAPI.prototype.format_search_status = function (status, args) {
440 if (!status.user && status.from_user) { // search data
450 status.user = {
46 screen_name: status.from_user,
47 profile_image_url: status.profile_image_url,
48 id: status.from_user_id
49 };
500 delete status.profile_image_url;
510 delete status.from_user;
520 delete status.from_user_id;
53 }
540 return this.format_status(status, args);
55};
56
571TSinaAPI.prototype.format_status = function (status, args) {
580 if (status.user) {
590 status.user = this.format_user(status.user, args);
60 }
61
620 if (status.retweeted_status) {
630 status.retweeted_status = this.format_status(status.retweeted_status, args);
64 }
650 if (status.user) {
660 status.t_url = 'http://weibo.com/' + status.user.id + '/' + weiboutil.mid2url(status.mid);
67 }
680 return status;
69};
70
711TSinaAPI.prototype.format_user = function (user, args) {
720 user.t_url = 'http://weibo.com/' + (user.domain || user.id);
730 if (user.status) {
740 user.status = this.format_status(user.status, args);
750 if (!user.status.t_url) {
760 user.status.t_url = 'http://weibo.com/' + user.id + '/' + weiboutil.mid2url(user.status.mid || user.status.id);
77 }
78 }
790 return user;
80};
81
821TSinaAPI.prototype.format_comment = function (comment, args) {
830 comment.user = this.format_user(comment.user, args);
840 comment.status = this.format_status(comment.status, args);
850 return comment;
86};
87
881TSinaAPI.prototype.format_message = function (message, args) {
890 message.sender = this.format_user(message.sender, args);
900 message.recipient = this.format_user(message.recipient, args);
910 return message;
92};
93
941TSinaAPI.prototype.format_emotion = function (emotion, args) {
950 emotion.title = emotion.phrase.substring(1, emotion.phrase.length - 1);
960 return emotion;
97};
98
99
100// rate_limit_status: function (data, callback, context) {
101// var params = {
102// url: this.config.rate_limit_status,
103// type: 'GET',
104// play_load: 'rate',
105// data: data
106// };
107// this._send_request(params, callback, context);
108// },
109
110// // since_id, max_id, count, page
111// friends_timeline: function (data, callback, context) {
112// var params = {
113// url: this.config.friends_timeline,
114// type: 'GET',
115// play_load: 'status',
116// data: data
117// };
118// this._send_request(params, callback, context);
119// },
120
121// // id, user_id, screen_name, since_id, max_id, count, page
122// user_timeline: function (data, callback, context) {
123// var params = {
124// url: this.config.user_timeline,
125// type: 'GET',
126// play_load: 'status',
127// data: data
128// };
129// this._send_request(params, callback, context);
130// },
131
132// // id, count, page
133// comments_timeline: function (data, callback, context) {
134// var params = {
135// url: this.config.comments_timeline,
136// type: 'GET',
137// play_load: 'comment',
138// data: data
139// };
140// this._send_request(params, callback, context);
141// },
142
143// // id, since_id, max_id, count, page
144// repost_timeline: function (data, callback, context) {
145// var params = {
146// url: this.config.repost_timeline,
147// type: 'GET',
148// play_load: 'status',
149// data: data
150// };
151// this._send_request(params, callback, context);
152// },
153
154// // since_id, max_id, count, page
155// mentions: function (data, callback, context){
156// var params = {
157// url: this.config.mentions,
158// type: 'GET',
159// play_load: 'status',
160// data: data
161// };
162// this._send_request(params, callback, context);
163// },
164
165// // id, user_id, screen_name, cursor, count
166// followers: function (data, callback, context) {
167// var params = {
168// url: this.config.followers,
169// type: 'GET',
170// play_load: 'user',
171// data: data
172// };
173// this._send_request(params, callback, context);
174// },
175
176// public_timeline: function (data, callback, context) {
177// var params = {
178// url: this.config.public_timeline,
179// type: 'GET',
180// play_load: 'status',
181// data: data
182// };
183// this._send_request(params, callback, context);
184// },
185
186// // id, user_id, screen_name, cursor, count
187// friends: function (data, callback, context) {
188// var params = {
189// url: this.config.friends,
190// type: 'GET',
191// play_load: 'user',
192// data: data
193// };
194// this._send_request(params, callback, context);
195// },
196
197// // page
198// favorites: function (data, callback, context) {
199// var params = {
200// url: this.config.favorites,
201// type: 'GET',
202// play_load: 'status',
203// data: data
204// };
205// this._send_request(params, callback, context);
206// },
207
208// // id
209// favorites_create: function (data, callback, context) {
210// var params = {
211// url: this.config.favorites_create,
212// type: 'POST',
213// play_load: 'status',
214// data: data
215// };
216// this._send_request(params, callback, context);
217// },
218
219// // id
220// favorites_destroy: function (data, callback, context) {
221// var params = {
222// url: this.config.favorites_destroy,
223// type: 'POST',
224// play_load: 'status',
225// data: data
226// };
227// this._send_request(params, callback, context);
228// },
229
230// // ids
231// counts: function (data, callback, context) {
232// var params = {
233// url: this.config.counts,
234// type: 'GET',
235// play_load: 'count',
236// data: data
237// };
238// this._send_request(params, callback, context);
239// },
240
241// // id
242// user_show: function (data, callback, context) {
243// var params = {
244// url: this.config.user_show,
245// type: 'GET',
246// play_load: 'user',
247// data: data
248// };
249// this._send_request(params, callback, context);
250// },
251
252// // since_id, max_id, count, page
253// direct_messages: function (data, callback, context) {
254// var params = {
255// url: this.config.direct_messages,
256// type: 'GET',
257// play_load: 'message',
258// data: data
259// };
260// this._send_request(params, callback, context);
261// },
262
263// // id
264// destroy_msg: function (data, callback, context) {
265// var params = {
266// url: this.config.destroy_msg,
267// type: 'POST',
268// play_load: 'message',
269// data: data
270// };
271// this._send_request(params, callback, context);
272// },
273
274// /*data的参数列表:
275// content 待发送消息的正文,请确定必要时需要进行URL编码 ( encode ) ,另外,不超过140英文或140汉字。
276// message 必须 0 表示悄悄话 1 表示戳一下
277// receiveUserId 必须,接收方的用户id
278// source 可选,显示在网站上的来自哪里对应的标识符。如果想显示指定的字符,请与官方人员联系。
279// */
280// new_message: function (data, callback, context) {
281// var params = {
282// url: this.config.new_message,
283// type: 'POST',
284// play_load: 'message',
285// data: data
286// };
287// this._send_request(params, callback, context);
288// },
289
290// // id
291// status_show: function (data, callback, context) {
292// var params = {
293// url: this.config.status_show,
294// play_load: 'status',
295// data: data
296// };
297// this._send_request(params, callback, context);
298// },
299
300// // 格式上传参数,方便子类覆盖做特殊处理
301// // 子类可以增加自己的参数
302// format_upload_params: function (user, data, pic) {
303
304// },
305
306
307
308// repost: function (data, callback, context) {
309// var params = {
310// url: this.config.repost,
311// type: 'POST',
312// play_load: 'status',
313// data: data
314// };
315// this._send_request(params, callback, context);
316// },
317
318// comment: function (data, callback, context) {
319// var params = {
320// url: this.config.comment,
321// type: 'POST',
322// play_load: 'comment',
323// data: data
324// };
325// this._send_request(params, callback, context);
326// },
327
328// reply: function (data, callback, context) {
329// var params = {
330// url: this.config.reply,
331// type: 'POST',
332// play_load: 'comment',
333// data: data
334// };
335// this._send_request(params, callback, context);
336// },
337
338// comments: function (data, callback, context) {
339// var params = {
340// url: this.config.comments,
341// type: 'GET',
342// play_load: 'comment',
343// data: data
344// };
345// this._send_request(params, callback, context);
346// },
347
348// // id
349// comment_destroy: function (data, callback, context) {
350// var params = {
351// url: this.config.comment_destroy,
352// type: 'POST',
353// play_load: 'comment',
354// data: data
355// };
356// this._send_request(params, callback, context);
357// },
358
359// friendships_create: function (data, callback, context) {
360// var params = {
361// url: this.config.friendships_create,
362// type: 'POST',
363// play_load: 'user',
364// data: data
365// };
366// this._send_request(params, callback, context);
367// },
368
369// // id
370// friendships_destroy: function (data, callback, context) {
371// var params = {
372// url: this.config.friendships_destroy,
373// type: 'POST',
374// play_load: 'user',
375// data: data
376// };
377// this._send_request(params, callback, context);
378// },
379
380// friendships_show: function (data, callback, context) {
381// var params = {
382// url: this.config.friendships_show,
383// play_load: 'user',
384// data: data
385// };
386// this._send_request(params, callback, context);
387// },
388
389// // type
390// reset_count: function (data, callback, context) {
391// var params = {
392// url: this.config.reset_count,
393// type: 'POST',
394// play_load: 'result',
395// data: data
396// };
397// this._send_request(params, callback, context);
398// },
399
400// // user_id, count, page
401// tags: function (data, callback, context) {
402// var params = {
403// url: this.config.tags,
404// play_load: 'tag',
405// data: data
406// };
407// this._send_request(params, callback, context);
408// },
409
410// // count, page
411// tags_suggestions: function (data, callback, context) {
412// var params = {
413// url: this.config.tags_suggestions,
414// play_load: 'tag',
415// data: data
416// };
417// this._send_request(params, callback, context);
418// },
419
420// // tags
421// create_tag: function (data, callback, context) {
422// var params = {
423// url: this.config.create_tag,
424// type: 'POST',
425// play_load: 'tag',
426// data: data
427// };
428// this._send_request(params, callback, context);
429// },
430
431// // tag_id
432// destroy_tag: function (data, callback, context) {
433// var params = {
434// url: this.config.destroy_tag,
435// type: 'POST',
436// play_load: 'tag',
437// data: data
438// };
439// this._send_request(params, callback, context);
440// },
441
442// // id
443// destroy: function (data, callback, context) {
444// if (!data || !data.id) {
445// return;
446// }
447// var params = {
448// url: this.config.destroy,
449// type: 'POST',
450// play_load: 'status',
451// data: data
452// };
453// this._send_request(params, callback, context);
454// },
455
456// // q, max_id, count
457// search: function (data, callback, context) {
458// var params = {
459// url: this.config.search,
460// play_load: 'status',
461// data: data
462// };
463// this._send_request(params, callback, context);
464// },
465
466// // q, page, count
467// user_search: function (data, callback, context) {
468// var params = {
469// url: this.config.user_search,
470// play_load: 'user',
471// data: data
472// };
473// this._send_request(params, callback, context);
474// },
475
476// /**
477// * List all emotions.
478// *
479// * @param {Object} user
480// * @param {Function(err, emotions)} callback
481// * - {Object} emotions: {
482// * '[哈哈]': {
483// * url: "http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/41/zz2_org.gif",
484// * type: "face",
485// * title: "哈哈",
486// * },
487// * ...
488// * }
489// * @param {Object} context, callback context
490// * @return this
491// */
492// emotions: function (user, callback, context) {
493// // http://api.t.sina.com.cn/emotions.json?&source=3538199806&language=cnname
494// // http://api.t.sina.com.cn/emotions.json?&source=3538199806&language=twname
495
496// var ep = EventProxy.create();
497// ep.after('emotions', this.config.emotion_types.length, function (datas) {
498// var emotions = {};
499// for (var i = 0, l = datas.length; i < l; i++) {
500// var items = datas[i];
501// if (!items) {
502// continue;
503// }
504// for (var j = 0, jl = items.length; j < jl; j++) {
505// var emotion = items[j];
506// emotions[emotion.phrase] = emotion;
507// }
508// }
509// callback.call(this, null, emotions);
510// });
511// ep.once('error', function (err) {
512// ep.unbind();
513// callback.call(this, err);
514// });
515// var that = this;
516// that.config.emotion_types.forEach(function (args) {
517// var data = {
518// user: user
519// };
520// for (var k in args) {
521// data[k] = args[k];
522// }
523// var params = {
524// url: that.config.emotions,
525// play_load: 'emotion',
526// need_source: true,
527// data: data
528// };
529// that._send_request(params, function (err, emotions) {
530// if (err) {
531// return ep.emit('error', err);
532// }
533// ep.emit('emotions', emotions);
534// });
535// });
536// return this;
537// },
538
539
540
541// URL_RE: new RegExp('(?:\\[url\\s*=\\s*|)((?:www\\.|http[s]?://)[\\w\\.\\?%&\\-/#=;:!\\+~]+)(?:\\](.+)\\[/url\\]|)', 'ig'),
542// /**
543// * format status.text to display
544// */
545// process_text: function (str_or_status, need_encode) {
546// var str = str_or_status;
547// if (need_encode === 'undedfined') {
548// need_encode = true;
549// }
550// if (str_or_status.text !== undefined) {
551// str = str_or_status.text;
552// }
553// if (str) {
554// if (need_encode) {
555// str = utils.htmlencode(str);
556// }
557// str = str.replace(this.URL_RE, this._replace_url_callback);
558// str = this.process_at(str, str_or_status); //@***
559// str = this.process_emotional(str);
560// str = this.process_search(str); //#xxXX#
561// // iPhone emoji
562// str = str.replace( /([\uE001-\uE537])/gi, this._get_iphone_emoji);
563// }
564// return str || '&nbsp;';
565// },
566// _replace_url_callback: function (m, g1, g2) {
567// var _url = g1;
568// if (g1.indexOf('http') !== 0) {
569// _url = 'http://' + g1;
570// }
571// return '<a target="_blank" class="link" href="{{url}}">{{value}}</a>'.format({
572// url: _url, title: g1, value: g2||g1
573// });
574// },
575
576// _get_iphone_emoji: function (str) {
577// return "<span class=\"iphoneEmoji "+ str.charCodeAt(0).toString(16).toUpperCase()+"\"></span>";
578// },
579
580// SEARCH_MATCH_RE: /#([^#]+)#/g,
581// SEARCH_TPL: '<a target="_blank" href="{{search_url}}{{search}}" title="Search #{{search}}">#{{search}}#</a>',
582
583// process_search: function (str) {
584// var that = this;
585// return str.replace(this.SEARCH_MATCH_RE, function (m, g1) {
586// return that._process_search_callback(m, g1);
587// });
588// },
589
590// _process_search_callback: function (m, g1) {
591// // 修复#xxx@xxx#嵌套问题
592// // var search = g1.remove_html_tag();
593// return this.SEARCH_TPL.format({ search: g1, search_url: this.config.search_url });
594// },
595
596// format_search_text: function (str) { // 格式化主题
597// return '#' + str.trim() + '#';
598// },
599
600// AT_RE: /@([\w\-\_\u2E80-\u3000\u303F-\u9FFF]+)/g,
601// process_at: function (str) {
602// //@*** u4e00-\u9fa5:中文字符 \u2E80-\u9FFF:中日韩字符
603// //【观点·@任志强】今年提出的1000万套的保障房任务可能根本完不成
604// // http://blog.oasisfeng.com/2006/10/19/full-cjk-unicode-range/
605// // CJK标点符号:3000-303F
606// var tpl = '<a class="at_user" data-name="$1" href="javascript:;" rhref="' +
607// this.config.user_home_url + '$1" title="show users">@$1</a>';
608// return str.replace(this.AT_RE, tpl);
609// },
610
611// process_emotional: function (str) {
612// var that = this;
613// return str.replace(/\[([\u4e00-\u9fff,\uff1f,\w]{1,4})\]/g, function (m, g1) {
614// return that._replace_emotional_callback(m, g1);
615// });
616// },
617
618// EMOTIONAL_TPL: '<img title="{{title}}" src="{{src}}" />',
619// _replace_emotional_callback: function (m, g1) {
620// if (g1) {
621// var face = this.EMOTIONS[g1];
622// if (face) {
623// return this.EMOTIONAL_TPL.format({ title: m, src: FACE_URL_PRE + face });
624// }
625// }
626// return m;
627// },
628
629// };
630
631
632// //新浪微博表情转化
633// var FACE_URL_PRE = TSinaAPI.FACE_URL_PRE = 'http://timg.sjs.sinajs.cn/t3/style/images/common/face/ext/normal/';
634// var FACE_TPL = TSinaAPI.FACE_TPL = '[{{name}}]';
635// var FACES = TSinaAPI.FACES = {
636// "呵呵": "eb/smile.gif",
637// "嘻嘻": "c2/tooth.gif",
638// "哈哈": "6a/laugh.gif",
639// "爱你": "7e/love.gif",
640// "晕": "a4/dizzy.gif",
641// "泪": "d8/sad.gif",
642// "馋嘴": "b8/cz_thumb.gif",
643// "抓狂": "4d/crazy.gif",
644// "哼": "19/hate.gif",
645// "可爱": "9c/tz_thumb.gif",
646// "怒": "57/angry.gif",
647// "æ±—": "13/sweat.gif",
648// "å›°": "8b/sleepy.gif",
649// "害羞": "05/shame_thumb.gif",
650// "睡觉": "7d/sleep_thumb.gif",
651// "é’±": "90/money_thumb.gif",
652// "偷笑": "7e/hei_thumb.gif",
653// "é…·": "40/cool_thumb.gif",
654// "è¡°": "af/cry.gif",
655// "吃惊": "f4/cj_thumb.gif",
656// "闭嘴": "29/bz_thumb.gif",
657// "鄙视": "71/bs2_thumb.gif",
658// "挖鼻屎": "b6/kbs_thumb.gif",
659// "花心": "64/hs_thumb.gif",
660// "鼓掌": "1b/gz_thumb.gif",
661// "失望": "0c/sw_thumb.gif",
662// "思考": "e9/sk_thumb.gif",
663// "生病": "b6/sb_thumb.gif",
664// "亲亲": "8f/qq_thumb.gif",
665// "怒骂": "89/nm_thumb.gif",
666// "太开心": "58/mb_thumb.gif",
667// "懒得理你": "17/ldln_thumb.gif",
668// "右哼哼": "98/yhh_thumb.gif",
669// "左哼哼": "6d/zhh_thumb.gif",
670// "嘘": "a6/x_thumb.gif",
671// "委屈": "73/wq_thumb.gif",
672// "吐": "9e/t_thumb.gif",
673// "可怜": "af/kl_thumb.gif",
674// "打哈气": "f3/k_thumb.gif",
675// "做鬼脸": "88/zgl_thumb.gif",
676// "握手": "0c/ws_thumb.gif",
677// "耶": "d9/ye_thumb.gif",
678// "good": "d8/good_thumb.gif",
679// "å¼±": "d8/sad_thumb.gif",
680// "不要": "c7/no_thumb.gif",
681// "ok": "d6/ok_thumb.gif",
682// "赞": "d0/z2_thumb.gif",
683// "来": "40/come_thumb.gif",
684// "蛋糕": "6a/cake.gif",
685// "心": "6d/heart.gif",
686// "伤心": "ea/unheart.gif",
687// "é’Ÿ": "d3/clock_thumb.gif",
688// "猪头": "58/pig.gif",
689// "å’–å•¡": "64/cafe_thumb.gif",
690// "话筒": "1b/m_thumb.gif",
691// "干杯": "bd/cheer.gif",
692// "绿丝带": "b8/green.gif",
693// "蜡烛": "cc/candle.gif",
694// "微风": "a5/wind_thumb.gif",
695// "月亮": "b9/moon.gif",
696// "月饼": "96/mooncake3_thumb.gif",
697// "满月": "5d/moon1_thumb.gif",
698// "酒壶": "64/wine_thumb.gif",
699// "团": "11/tuan_thumb.gif",
700// "圆": "53/yuan_thumb.gif",
701// "左抱抱": "54/left_thumb.gif",
702// "右抱抱": "0d/right_thumb.gif",
703// "乐乐": "66/guanbuzhao_thumb.gif",
704// "团圆月饼": "e6/tuanyuan_thumb.gif",
705// "å¿«å¿«": "49/lbq1_thumb.gif",
706// "织": "41/zz2_thumb.gif",
707// "å›´è§‚": "f2/wg_thumb.gif",
708// "威武": "70/vw_thumb.gif",
709// "爱心专递": "c9/axcd_thumb.gif",
710// "奥特曼": "bc/otm_thumb.gif",
711// //亚运
712// "国旗": "dc/flag_thumb.gif",
713// "金牌": "f4/jinpai_thumb.gif",
714// "银牌": "1e/yinpai_thumb.gif",
715// "铜牌": "26/tongpai_thumb.gif",
716// "å›´è„–": "3f/weijin_thumb.gif",
717// "温暖帽子": "f1/wennuanmaozi_thumb.gif",
718// "手套": "72/shoutao_thumb.gif",
719// "落叶": "79/yellowMood_thumb.gif",
720// "照相机": "33/camera_thumb.gif",
721// "白云": "ff/y3_thumb.gif",
722// "礼物": "c4/liwu_thumb.gif",
723// "v5": "c5/v5_org.gif",
724// "书呆子": "61/sdz_org.gif"
725// };
726
727// // http://api.t.sina.com.cn/emotions.json
728// TSinaAPI.EMOTIONS = {
729// "呵呵": "eb/smile.gif", "嘻嘻": "c2/tooth.gif", "哈哈": "6a/laugh.gif", "爱你": "7e/love.gif", "晕": "a4/dizzy.gif", "泪": "d8/sad.gif", "馋嘴": "b8/cz_org.gif", "抓狂": "4d/crazy.gif", "哼": "19/hate.gif", "可爱": "9c/tz_org.gif", "怒": "57/angry.gif", "汗": "13/sweat.gif", "困": "8b/sleepy.gif", "害羞": "05/shame_org.gif", "睡觉": "7d/sleep_org.gif", "钱": "90/money_org.gif", "偷笑": "7e/hei_org.gif", "酷": "40/cool_org.gif", "衰": "af/cry.gif", "吃惊": "f4/cj_org.gif", "闭嘴": "29/bz_org.gif", "鄙视": "71/bs2_org.gif", "挖鼻屎": "b6/kbs_org.gif", "花心": "64/hs_org.gif", "鼓掌": "1b/gz_org.gif", "失望": "0c/sw_org.gif", "思考": "e9/sk_org.gif", "生病": "b6/sb_org.gif", "亲亲": "8f/qq_org.gif", "怒骂": "89/nm_org.gif", "太开心": "58/mb_org.gif", "懒得理你": "17/ldln_org.gif", "右哼哼": "98/yhh_org.gif", "左哼哼": "6d/zhh_org.gif", "嘘": "a6/x_org.gif", "委屈": "73/wq_org.gif", "吐": "9e/t_org.gif", "可怜": "af/kl_org.gif", "打哈气": "f3/k_org.gif", "顶": "91/d_org.gif", "疑问": "5c/yw_org.gif", "做鬼脸": "88/zgl_org.gif", "握手": "0c/ws_org.gif", "耶": "d9/ye_org.gif", "good": "d8/good_org.gif", "弱": "d8/sad_org.gif", "不要": "c7/no_org.gif", "ok": "d6/ok_org.gif", "赞": "d0/z2_org.gif", "来": "40/come_org.gif", "蛋糕": "6a/cake.gif", "心": "6d/heart.gif", "伤心": "ea/unheart.gif", "钟": "d3/clock_org.gif", "猪头": "58/pig.gif", "咖啡": "64/cafe_org.gif", "话筒": "1b/m_org.gif", "月亮": "b9/moon.gif", "太阳": "e5/sun.gif", "干杯": "bd/cheer.gif", "微风": "a5/wind_org.gif", "飞机": "6d/travel_org.gif", "兔子": "81/rabbit_org.gif", "熊猫": "6e/panda_org.gif", "给力": "c9/geili_org.gif", "神马": "60/horse2_org.gif", "浮云": "bc/fuyun_org.gif", "织": "41/zz2_org.gif", "围观": "f2/wg_org.gif", "威武": "70/vw_org.gif", "奥特曼": "bc/otm_org.gif", "实习": "48/sx_org.gif", "自行车": "46/zxc_org.gif", "照相机": "33/camera_org.gif", "叶子": "b8/green_org.gif", "春暖花开": "ca/chunnuanhuakai_org.gif", "咆哮": "4b/paoxiao_org.gif", "彩虹": "03/ch_org.gif", "沙尘暴": "69/sc_org.gif", "地球一小时": "4f/diqiuxiuxiyixiaoshi_org.gif", "爱心传递": "c9/axcd_org.gif", "蜡烛": "cc/candle.gif", "绿丝带": "b8/green.gif", "挤眼": "c3/zy_org.gif", "亲亲": "8f/qq_org.gif", "怒骂": "89/nm_org.gif", "太开心": "58/mb_org.gif", "懒得理你": "17/ldln_org.gif", "打哈气": "f3/k_org.gif", "生病": "b6/sb_org.gif", "书呆子": "61/sdz_org.gif", "失望": "0c/sw_org.gif", "可怜": "af/kl_org.gif", "挖鼻屎": "b6/kbs_org.gif", "黑线": "91/h_org.gif", "花心": "64/hs_org.gif", "可爱": "9c/tz_org.gif", "吐": "9e/t_org.gif", "委屈": "73/wq_org.gif", "思考": "e9/sk_org.gif", "哈哈": "6a/laugh.gif", "嘘": "a6/x_org.gif", "右哼哼": "98/yhh_org.gif", "左哼哼": "6d/zhh_org.gif", "疑问": "5c/yw_org.gif", "阴险": "6d/yx_org.gif", "做鬼脸": "88/zgl_org.gif", "爱你": "7e/love.gif", "馋嘴": "b8/cz_org.gif", "顶": "91/d_org.gif", "钱": "90/money_org.gif", "嘻嘻": "c2/tooth.gif", "汗": "13/sweat.gif", "呵呵": "eb/smile.gif", "睡觉": "7d/sleep_org.gif", "困": "8b/sleepy.gif", "害羞": "05/shame_org.gif", "悲伤": "1a/bs_org.gif", "鄙视": "71/bs2_org.gif", "抱抱": "7c/bb_org.gif", "拜拜": "70/88_org.gif", "怒": "57/angry.gif", "吃惊": "f4/cj_org.gif", "闭嘴": "29/bz_org.gif", "泪": "d8/sad.gif", "偷笑": "7e/hei_org.gif", "哼": "19/hate.gif", "晕": "a4/dizzy.gif", "衰": "af/cry.gif", "抓狂": "4d/crazy.gif", "愤怒": "bd/fn_org.gif", "感冒": "a0/gm_org.gif", "鼓掌": "1b/gz_org.gif", "酷": "40/cool_org.gif", "来": "40/come_org.gif", "good": "d8/good_org.gif", "haha": "13/ha_org.gif", "不要": "c7/no_org.gif", "ok": "d6/ok_org.gif", "拳头": "cc/o_org.gif", "弱": "d8/sad_org.gif", "握手": "0c/ws_org.gif", "赞": "d0/z2_org.gif", "耶": "d9/ye_org.gif", "最差": "3e/bad_org.gif", "右抱抱": "0d/right_org.gif", "左抱抱": "54/left_org.gif", "粉红丝带": "77/pink_org.gif", "爱心传递": "c9/axcd_org.gif", "心": "6d/heart.gif", "绿丝带": "b8/green.gif", "蜡烛": "cc/candle.gif", "围脖": "3f/weijin_org.gif", "温暖帽子": "f1/wennuanmaozi_org.gif", "手套": "72/shoutao_org.gif", "红包": "71/hongbao_org.gif", "喜": "bf/xi_org.gif", "礼物": "c4/liwu_org.gif", "蛋糕": "6a/cake.gif", "钻戒": "31/r_org.gif", "钻石": "9f/diamond_org.gif", "大巴": "9c/dynamicbus_org.gif", "飞机": "6d/travel_org.gif", "自行车": "46/zxc_org.gif", "汽车": "a4/jc_org.gif", "手机": "4b/sj2_org.gif", "照相机": "33/camera_org.gif", "药": "5d/y_org.gif", "电脑": "df/dn_org.gif", "手纸": "55/sz_org.gif", "落叶": "79/yellowMood_org.gif", "圣诞树": "a2/christree_org.gif", "圣诞帽": "06/chrishat_org.gif", "圣诞老人": "c5/chrisfather_org.gif", "圣诞铃铛": "64/chrisbell_org.gif", "圣诞袜": "08/chrisocks_org.gif", "图片": "ce/tupianimage_org.gif", "六芒星": "c2/liumangxing_org.gif", "地球一小时": "4f/diqiuxiuxiyixiaoshi_org.gif", "植树节": "56/zhishujie_org.gif", "粉蛋糕": "bf/nycake_org.gif", "糖果": "34/candy_org.gif", "万圣节": "73/nanguatou2_org.gif", "火炬": "3b/hj_org.gif", "酒壶": "64/wine_org.gif", "月饼": "96/mooncake3_org.gif", "满月": "5d/moon1_org.gif", "巧克力": "b1/qkl_org.gif", "脚印": "12/jy_org.gif", "酒": "39/j2_org.gif", "狗": "5d/g_org.gif", "工作": "b2/gz3_org.gif", "档案": "ce/gz2_org.gif", "叶子": "b8/green_org.gif", "钢琴": "b2/gq_org.gif", "印迹": "84/foot_org.gif", "钟": "d3/clock_org.gif", "茶": "a8/cha_org.gif", "西瓜": "6b/watermelon.gif", "雨伞": "33/umb_org.gif", "电视机": "b3/tv_org.gif", "电话": "9d/tel_org.gif", "太阳": "e5/sun.gif", "星": "0b/star_org.gif", "哨子": "a0/shao.gif", "话筒": "1b/m_org.gif", "音乐": "d0/music_org.gif", "电影": "77/movie_org.gif", "月亮": "b9/moon.gif", "唱歌": "79/ktv_org.gif", "冰棍": "3a/ice.gif", "房子": "d1/house_org.gif", "帽子": "25/hat_org.gif", "足球": "c0/football.gif", "鲜花": "6c/flower_org.gif", "花": "6c/flower.gif", "风扇": "92/fan.gif", "干杯": "bd/cheer.gif", "咖啡": "64/cafe_org.gif", "兔子": "81/rabbit_org.gif", "神马": "60/horse2_org.gif", "浮云": "bc/fuyun_org.gif", "给力": "c9/geili_org.gif", "萌": "42/kawayi_org.gif", "鸭梨": "bb/pear_org.gif", "熊猫": "6e/panda_org.gif", "互粉": "89/hufen_org.gif", "织": "41/zz2_org.gif", "围观": "f2/wg_org.gif", "扔鸡蛋": "91/rjd_org.gif", "奥特曼": "bc/otm_org.gif", "威武": "70/vw_org.gif", "伤心": "ea/unheart.gif", "热吻": "60/rw_org.gif", "囧": "15/j_org.gif", "orz": "c0/orz1_org.gif", "宅": "d7/z_org.gif", "小丑": "6b/xc_org.gif", "帅": "36/s2_org.gif", "猪头": "58/pig.gif", "实习": "48/sx_org.gif", "骷髅": "bd/kl2_org.gif", "便便": "34/s_org.gif", "雪人": "d9/xx2_org.gif", "黄牌": "a0/yellowcard.gif", "红牌": "64/redcard.gif", "跳舞花": "70/twh_org.gif", "礼花": "3d/bingo_org.gif", "打针": "b0/zt_org.gif", "叹号": "3b/th_org.gif", "问号": "9d/wh_org.gif", "句号": "9b/jh_org.gif", "逗号": "cc/dh_org.gif", "1": "9b/1_org.gif", "2": "2c/2_org.gif", "3": "f3/3_org.gif", "4": "2c/4_org.gif", "5": "d5/5_org.gif", "6": "dc/6_org.gif", "7": "43/7_org.gif", "8": "6d/8_org.gif", "9": "26/9_org.gif", "0": "d8/ling_org.gif", "闪": "ce/03_org.gif", "啦啦": "c1/04_org.gif", "吼吼": "34/05_org.gif", "庆祝": "67/06_org.gif", "嘿": "d3/01_org.gif", "省略号": "0d/shengluehao_org.gif", "kiss": "59/kiss2_org.gif", "圆": "53/yuan_org.gif", "团": "11/tuan_org.gif", "团圆月饼": "e6/tuanyuan_org.gif", "欢欢": "c3/liaobuqi_org.gif", "乐乐": "66/guanbuzhao_org.gif", "管不着爱": "78/2guanbuzhao1_org.gif", "爱": "09/ai_org.gif", "了不起爱": "11/2liaobuqiai_org.gif", "有点困": "68/youdiankun_org.gif", "yes": "9e/yes_org.gif", "咽回去了": "72/yanhuiqule_org.gif", "鸭梨很大": "01/yalihenda_org.gif", "羞羞": "42/xiuxiu_org.gif", "喜欢你": "6b/xihuang_org.gif", "小便屁": "a0/xiaobianpi_org.gif", "无奈": "d6/wunai22_org.gif", "兔兔": "da/tutu_org.gif", "吐舌头": "98/tushetou_org.gif", "头晕": "48/touyun_org.gif", "听音乐": "d3/tingyinyue_org.gif", "睡大觉": "65/shuijiao_org.gif", "闪闪紫": "9e/shanshanzi_org.gif", "闪闪绿": "a8/shanshanlu_org.gif", "闪闪灰": "1e/shanshanhui_org.gif", "闪闪红": "10/shanshanhong_org.gif", "闪闪粉": "9d/shanshanfen_org.gif", "咆哮": "4b/paoxiao_org.gif", "摸头": "2c/motou_org.gif", "真美好": "d2/meihao_org.gif", "脸红自爆": "d8/lianhongzibao_org.gif", "哭泣女": "1c/kuqinv_org.gif", "哭泣男": "38/kuqinan_org.gif", "空": "fd/kong_org.gif", "尽情玩": "9f/jinqingwan_org.gif", "惊喜": "b8/jingxi_org.gif", "惊呆": "58/jingdai_org.gif", "胡萝卜": "e1/huluobo_org.gif", "欢腾去爱": "63/huangtengquai_org.gif", "感冒了": "67/ganmao_org.gif", "怒了": "ef/fennu_org.gif", "我要奋斗": "a6/fendou123_org.gif", "发芽": "95/faya_org.gif", "春暖花开": "ca/chunnuanhuakai_org.gif", "抽烟": "83/chouyan_org.gif", "昂": "31/ang_org.gif", "啊": "12/aa_org.gif", "自插双目": "d3/zichashuangmu_org.gif", "咦": "9f/yiwen_org.gif", "嘘嘘": "cf/xu_org.gif", "我吃": "00/wochiwode_org.gif", "喵呜": "a7/weiqu_org.gif", "v5": "c5/v5_org.gif", "调戏": "f7/tiaoxi_org.gif", "打牙": "d7/taihaoxiaole_org.gif", "手贱": "b8/shoujian_org.gif", "色": "a1/se_org.gif", "喷": "4a/pen_org.gif", "你懂的": "2e/nidongde_org.gif", "喵": "a0/miaomiao_org.gif", "美味": "c1/meiwei_org.gif", "惊恐": "46/jingkong_org.gif", "感动": "7c/gandong_org.gif", "放开": "55/fangkai_org.gif", "痴呆": "e8/chidai_org.gif", "扯脸": "99/chelian_org.gif", "不知所措": "ab/buzhisuocuo_org.gif", "白眼": "24/baiyan_org.gif", "猥琐": "e1/weisuo_org.gif", "挑眉": "c9/tiaomei_org.gif", "挑逗": "3c/tiaodou_org.gif", "亲耳朵": "1c/qinerduo_org.gif", "媚眼": "32/meiyan_org.gif", "冒个泡": "32/maogepao_org.gif", "囧耳朵": "f0/jiongerduo_org.gif", "鬼脸": "14/guilian_org.gif", "放电": "fd/fangdian_org.gif", "悲剧": "ea/beiju_org.gif", "抚摸": "78/touch_org.gif", "大汗": "13/sweat_org.gif", "大惊": "74/suprise_org.gif", "惊哭": "0c/supcry_org.gif", "星星眼": "5c/stareyes_org.gif", "好困": "8b/sleepy_org.gif", "呕吐": "75/sick_org.gif", "加我一个": "ee/plus1_org.gif", "痞痞兔耶": "19/pipioye_org.gif", "mua": "c6/muamua_org.gif", "面抽": "fd/mianchou_org.gif", "大笑": "6a/laugh_org.gif", "揉": "d6/knead_org.gif", "痞痞兔囧": "38/jiong_org.gif", "哈尼兔耶": "53/honeyoye_org.gif", "开心": "40/happy_org.gif", "咬手帕": "af/handkerchief_org.gif", "去": "6b/go_org.gif", "晕死了": "a4/dizzy_org.gif", "大哭": "af/cry_org.gif", "扇子遮面": "a1/coverface_org.gif", "怒气": "ea/angery_org.gif", "886": "6f/886_org.gif", "雾": "68/w_org.gif", "台风": "55/tf_org.gif", "沙尘暴": "69/sc_org.gif", "晴转多云": "d2/qzdy_org.gif", "流星": "8e/lx_org.gif", "龙卷风": "6a/ljf_org.gif", "洪水": "ba/hs2_org.gif", "风": "74/gf_org.gif", "多云转晴": "f3/dyzq_org.gif", "彩虹": "03/ch_org.gif", "冰雹": "05/bb2_org.gif", "微风": "a5/wind_org.gif", "阳光": "1a/sunny_org.gif", "雪": "00/snow_org.gif", "闪电": "e3/sh_org.gif", "下雨": "50/rain.gif", "阴天": "37/dark_org.gif", "白羊": "07/byz2_org.gif", "射手": "46/ssz2_org.gif", "双鱼": "e2/syz2_org.gif", "双子": "89/szz2_org.gif", "天秤": "6b/tpz2_org.gif", "天蝎": "1e/txz2_org.gif", "水瓶": "1b/spz2_org.gif", "处女": "62/cnz2_org.gif", "金牛": "3b/jnz2_org.gif", "巨蟹": "d2/jxz2_org.gif", "狮子": "4a/leo2_org.gif", "摩羯": "16/mjz2_org.gif", "天蝎座": "09/txz_org.gif", "天秤座": "c1/tpz_org.gif", "双子座": "d4/szz_org.gif", "双鱼座": "7f/syz_org.gif", "射手座": "5d/ssz_org.gif", "水瓶座": "00/spz_org.gif", "摩羯座": "da/mjz_org.gif", "狮子座": "23/leo_org.gif", "巨蟹座": "a3/jxz_org.gif", "金牛座": "8d/jnz_org.gif", "处女座": "09/cnz_org.gif", "白羊座": "e0/byz_org.gif", "yeah": "1a/yeah_org.gif", "喜欢": "5f/xh_org.gif", "心动": "5f/xd_org.gif", "无聊": "53/wl_org.gif", "手舞足蹈": "b2/gx_org.gif", "搞笑": "09/gx2_org.gif", "痛哭": "eb/gd_org.gif", "爆发": "38/fn2_org.gif", "发奋": "31/d2_org.gif", "不屑": "b0/bx_org.gif", "加油": "d4/jiayou_org.gif", "国旗": "dc/flag_org.gif", "金牌": "f4/jinpai_org.gif", "银牌": "1e/yinpai_org.gif", "铜牌": "26/tongpai_org.gif", "哨子": "a0/shao.gif", "黄牌": "a0/yellowcard.gif", "红牌": "64/redcard.gif", "足球": "c0/football.gif", "篮球": "2c/bball_org.gif", "黑8": "6b/black8_org.gif", "排球": "cf/volleyball_org.gif", "游泳": "b9/swimming_org.gif", "乒乓球": "a5/pingpong_org.gif", "投篮": "7a/basketball_org.gif", "羽毛球": "77/badminton_org.gif", "射门": "e0/zuqiu_org.gif", "射箭": "40/shejian_org.gif", "举重": "14/juzhong_org.gif", "击剑": "38/jijian_org.gif", "烦躁": "c5/fanzao_org.gif", "呲牙": "c1/ciya_org.gif", "有钱": "e6/youqian_org.gif", "微笑": "05/weixiao_org.gif", "帅爆": "c1/shuaibao_org.gif", "生气": "0a/shengqi_org.gif", "生病了": "19/shengbing_org.gif", "色眯眯": "90/semimi_org.gif", "疲劳": "d1/pilao_org.gif", "瞄": "14/miao_org.gif", "哭": "79/ku_org.gif", "好可怜": "76/kelian_org.gif", "紧张": "75/jinzhang_org.gif", "惊讶": "dc/jingya_org.gif", "激动": "bb/jidong_org.gif", "见钱": "2b/jianqian_org.gif", "汗了": "7d/han_org.gif", "奋斗": "4e/fendou_org.gif", "小人得志": "09/xrdz_org.gif", "哇哈哈": "cc/whh_org.gif", "叹气": "90/tq_org.gif", "冻结": "d3/sjdj_org.gif", "切": "1d/q_org.gif", "拍照": "ec/pz_org.gif", "怕怕": "7c/pp_org.gif", "怒吼": "4d/nh_org.gif", "膜拜": "9f/mb2_org.gif", "路过": "70/lg_org.gif", "泪奔": "34/lb_org.gif", "脸变色": "cd/lbs_org.gif", "亲": "05/kiss_org.gif", "恐怖": "86/kb_org.gif", "交给我吧": "e2/jgwb_org.gif", "欢欣鼓舞": "2b/hxgw_org.gif", "高兴": "c7/gx3_org.gif", "尴尬": "43/gg_org.gif", "发嗲": "4e/fd_org.gif", "犯错": "19/fc_org.gif", "得意": "fb/dy_org.gif", "吵闹": "fa/cn_org.gif", "冲锋": "2f/cf_org.gif", "抽耳光": "eb/ceg_org.gif", "差得远呢": "ee/cdyn_org.gif", "被砸": "5a/bz2_org.gif", "拜托": "6e/bt_org.gif", "必胜": "cf/bs3_org.gif", "不关我事": "e8/bgws_org.gif", "上火": "64/bf_org.gif", "不倒翁": "b6/bdw_org.gif", "不错哦": "79/bco_org.gif", "眨眨眼": "3b/zy2_org.gif", "杂技": "ec/zs_org.gif", "多问号": "17/wh2_org.gif", "跳绳": "79/ts_org.gif", "强吻": "b1/q3_org.gif", "不活了": "37/lb2_org.gif", "磕头": "6a/kt_org.gif", "呜呜": "55/bya_org.gif", "不": "a2/bx2_org.gif", "狂笑": "d5/zk_org.gif", "冤": "5f/wq2_org.gif", "蜷": "87/q2_org.gif", "美好": "ae/mh_org.gif", "乐和": "5f/m2_org.gif", "揪耳朵": "15/j3_org.gif", "晃": "bf/h2_org.gif", "high": "e7/f_org.gif", "蹭": "33/c_org.gif", "抱枕": "f4/bz3_org.gif", "不公平": "85/bgp_org.gif"
730// };

tbase.js

90%
424
384
40
LineHitsSource
1/*!
2 * node-weibo - lib/tbase.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var querystring = require('querystring');
141var urllib = require('urllib');
151var utils = require('./utils');
161var OAuth = require('./oauth');
171var emoji = require('emoji');
18
19/**
20 * TAPI Base class, support OAuth v1.0
21 */
221function TBase() {
234 this.config = {
24 host: 'api start url',
25 result_format: '.json',
26 appkey: '',
27 secret: '',
28 oauth_host: '',
29 oauth_callback: 'oob or url',
30 oauth_version: '1.0',
31
32 userinfo_has_counts: true, // 用户信息中是否包含粉丝数、微博数等信息
33 support_counts: true, // 是否支持批量获取转发和评论数
34 support_comment: true, // 判断是否支持评论列表
35 support_do_comment: true, // 判断是否支持发送评论
36 support_repost_comment: true, // 判断是否支持转发同时发评论
37 support_repost_comment_to_root: false, // 判断是否支持转发同时给原文作者发评论
38 support_upload: true, // 是否支持上传图片
39 support_repost: true, // 是否支持新浪形式转载
40 repost_pre: '转:', // 转发前缀
41 repost_delimiter: '//', //转发的分隔符
42 image_shorturl_pre: ' [图] ', // RT图片缩址前缀
43 support_favorites: true, // 判断是否支持收藏列表
44 support_do_favorite: true, // 判断是否支持收藏功能
45 support_geo: true, //是否支持地理位置信息上传
46 // 是否支持max_id 分页
47 support_max_id: true,
48 support_destroy_msg: true, //是否支持删除私信
49 support_direct_messages: true,
50 support_sent_direct_messages: true, //是否支持自己发送的私信
51 support_mentions: true,
52 support_friendships_create: true,
53 support_search: true,
54 support_search_max_id: false,
55 support_favorites_max_id: false, // 收藏分页使用max_id
56
57 need_processMsg: true, //是否需要处理消息的内容
58 comment_need_user_id: false, // 评论是否需要使用到用户id,默认为false,兼容所有旧接口
59
60 // api
61 public_timeline: '/statuses/public_timeline',
62 home_timeline: '/statuses/home_timeline',
63 user_timeline: '/statuses/user_timeline',
64 mentions: '/statuses/mentions',
65 comments_timeline: '/comments/timeline',
66 comments_mentions: '/comments/mentions',
67 comments_to_me: '/comments/to_me',
68 comments_by_me: '/comments/by_me',
69
70 repost_timeline: '/statuses/repost_timeline',
71 comments: '/statuses/comments',
72
73 show: '/statuses/show',
74 count: '/statuses/count',
75 update: '/statuses/update',
76 upload: '/statuses/upload',
77 repost: '/statuses/repost',
78 destroy: '/statuses/destroy',
79
80 followers: '/statuses/followers',
81 friends: '/statuses/friends',
82 favorites: '/favorites',
83 favorites_create: '/favorites/create',
84 favorites_destroy: '/favorites/destroy/{{id}}',
85
86 comment_create: '/statuses/comment',
87 comment_reply: '/statuses/reply',
88 comment_destroy: '/statuses/comment_destroy',
89
90 destroy_msg: '/direct_messages/destroy/{{id}}',
91 direct_messages: '/direct_messages',
92 sent_direct_messages: '/direct_messages/sent', //自己发送的私信列表,我当时为什么要命名为sent_direct_messages捏,我擦
93 new_message: '/direct_messages/new',
94 verify_credentials: '/account/verify_credentials',
95 user_show: '/users/show',
96 rate_limit_status: '/account/rate_limit_status',
97 friendships_create: '/friendships/create',
98 friendships_destroy: '/friendships/destroy',
99 friendships_show: '/friendships/show',
100 reset_count: '/statuses/reset_count',
101 emotions: '/emotions',
102
103 // 用户标签
104 tags: '/tags',
105 create_tag: '/tags/create',
106 destroy_tag: '/tags/destroy',
107 tags_suggestions: '/tags/suggestions',
108
109 // 搜索
110 search: '/statuses/search',
111 user_search: '/users/search',
112
113 oauth_authorize: '/oauth/authorize',
114 oauth_request_token: '/oauth/request_token',
115 oauth_access_token: '/oauth/access_token',
116
117 // 图片上传字段名称
118 pic_field: 'pic',
119 };
120}
121
1221module.exports = TBase;
123
1241TBase.prototype.init = function (config) {
1254 for (var k in config) {
12699 this.config[k] = config[k];
127 }
128};
129
130/**
131 * Utils methods
132 */
133
1341TBase.prototype.URL_RE = new RegExp('(?:\\[url\\s*=\\s*|)((?:www\\.|http[s]?://)[\\w\\.\\?%&\\-/#=;:!\\+~]+)(?:\\](.+)\\[/url\\]|)', 'ig');
135
1361TBase.prototype.process_text = function (status) {
1376 var text = status.text;
1386 if (!text) {
1393 return '&nbsp;';
140 }
1413 text = utils.escape(text);
1423 text = text.replace(this.URL_RE, this._replace_url);
143
1443 text = this.process_at(text, status); //@***
145
1463 text = this.process_emotional(text, status);
147
1483 text = this.process_search(text, status); //#xxXX#
149
1503 text = emoji.unifiedToHTML(emoji.softbankToUnified(text));
1513 return text || '&nbsp;';
152};
153
1541TBase.prototype._replace_url = function (m, g1, g2) {
1552 var url = g1;
1562 if (g1.indexOf('http') !== 0) {
1571 url = 'http://' + g1;
158 }
1592 var tpl = '<a target="_blank" class="link" href="{{url}}">{{value}}</a>';
1602 return utils.format(tpl, { url: url, title: g1, value: g2 || g1 });
161};
162
1631TBase.prototype.SearchMatchReg = /#([^#]+)#/g;
1641TBase.prototype.process_search = function (text, status) {
1656 var url = this.config.search_url;
1666 return text.replace(this.SearchMatchReg, function (m, g1) {
167 // fixed #xxx@xxx# nesting problem
1688 var search = utils.removeHTML(g1);
1698 var tpl = '<a target="_blank" href="{{url}}{{searchEncode}}" title="Search #{{search}}#">#{{search}}#</a>';
1708 return utils.format(tpl, {search: search, searchEncode: encodeURIComponent(search), url: url});
171 });
172};
173
174// // return [[hash1, hash_value], ..., [#xxx#, xxx]]
175// TBase.prototype.findSearchText = function (text, status) {
176// var matchs = text.match(this.SearchMatchReg);
177// var result = [];
178// if (matchs) {
179// for (var i = 0, len = matchs.length; i < len; i++) {
180// var s = matchs[i];
181// result.push([s, s.substring(1, s.length - 1)]);
182// }
183// }
184// return result;
185// };
186
187// TBase.prototype.formatSearchText = function (str) { // 格式化主题
188// return '#' + str.trim() + '#';
189// };
190
1911TBase.prototype._at_match_rex = /@([●\w\-\_\u2E80-\u3000\u303F-\u9FFF]+)/g;
1921TBase.prototype.process_at = function (text, status) {
193 // Handle no-ascii
194 //@*** u4e00-\u9fa5:中文字符 \u2E80-\u9FFF:中日韩字符
195 //【观点·@任志强】今年提出的1000万套的保障房任务可能根本完不成
196 // http://blog.oasisfeng.com/2006/10/19/full-cjk-unicode-range/
197 // CJK标点符号:3000-303F
1985 var homeurl = this.config.user_home_url;
1995 return text.replace(this._at_match_rex, function (m, g1) {
2004 var url = homeurl + encodeURIComponent(g1);
2014 return '<a class="at-user-link" href="' + url + '">@' + g1 + '</a>';
202 });
203};
204
205// // 获取str里面所有@用户的名称列表,不包含@符合
206// find_at_users: function (str) {
207// if (!str) {
208// return null;
209// }
210// var matchs = str.match(this._at_match_rex);
211// if (matchs) {
212// var users = [];
213// for(var i = 0, l = matchs.length; i < l; i++) {
214// var name = matchs[i].substring(1);
215// if(users.indexOf(name) < 0) {
216// users.push(name);
217// }
218// }
219// return users;
220// }
221// return null;
222// },
223
2241TBase.prototype.process_emotional = function (text, status) {
2253 return text.replace(/\[([\u4e00-\u9fff,\uff1f,\w]{1,6})\]/g, this._replace_emotional.bind(this));
226};
227
2281TBase.prototype.EMOTION_TPL = '<img title="{{title}}" src="{{src}}" />';
229
2301TBase.prototype._replace_emotional = function (m, g1) {
2310 if (!g1) {
2320 return m;
233 }
234 // if (!this.bgEmotions && typeof getBackgroundView !== 'undefined') {
235 // this.bgEmotions = getBackgroundView().Emotions;
236 // if (this.bgEmotions) {
237 // this.bgEmotions = this.bgEmotions.weibo;
238 // }
239 // }
240 // var face = this.bgEmotions && this.bgEmotions[g1];
241 // if (!face) {
242 // face = TSINA_API_EMOTIONS[g1];
243 // if (face) {
244 // face = TSINA_FACE_URL_PRE + face;
245 // }
246 // }
247 // if (face) {
248 // return this.EMOTION_TPL.format({ title: m, src: face });
249 // }
2500 return m;
251};
252
2531TBase.prototype.url_encode = function (text) {
2548 return encodeURIComponent(text);
255};
256
2571TBase.prototype._timeline = function (request_method, user, cursor, callback, playload) {
25832 cursor = this.convert_cursor(cursor);
25932 var params = {
260 type: 'GET',
261 playload: playload || 'status[]',
262 user: user,
263 data: cursor,
264 request_method: request_method
265 };
26632 var url = this.config[request_method];
26732 this.send_request(url, params, callback);
26832 return this;
269};
270
2711TBase.prototype.errorname = function (name) {
272 // get_access_token => GetAccessTokenError
27321 name = name.replace(/(?:^\w|\_\w)/g, function (m) {
27435 if (m.length === 2) {
27514 m = m.substring(1);
276 }
27735 return m.toUpperCase();
278 });
27921 return name + 'Error';
280};
281
2821TBase.prototype.detect_error = function (method, res, playload, data) {
28379 if (res.statusCode === 200) {
28469 return null;
285 }
28610 var errMessage = null;
28710 if (method === 'get_request_token' || method === 'get_access_token') {
2881 var token = querystring.parse(data);
2891 if (token) {
2901 errMessage = 'Get request token error, ' + (token.error_CN || token.error || data);
291 } else {
2920 errMessage = 'Get request token error, empty token string';
293 }
294 } else {
2959 errMessage = data.error_CN || data.error || data.message || data;
296 }
29710 var err = new Error(errMessage);
29810 err.data = data;
29910 err.name = this.errorname(method);
30010 return err;
301};
302
303/**
304 * 封装所有http请求,自动区分处理http和https
305 * args: {
306 * data,
307 * type: 'GET | POST | DELETE',
308 * headers
309 * }
310 * callback.call(context, data, error, res || xhr)
311 */
3121TBase.prototype.request = function (url, args, callback) {
31389 var playload = args.playload;
31489 if (playload !== 'string') {
31584 args.dataType = 'json';
316 }
31789 var self = this;
31889 urllib.request(url, args, function (err, data, res) {
31989 if (err) {
3200 return callback(err);
321 }
32289 if (playload === 'string') {
3235 data = data.toString();
324 }
325 // console.log(url, args, res.headers, res.statusCode, data)
326 // console.log(data.data)
32789 err = self.detect_error(args.request_method, res, playload, data);
32889 if (err) {
32920 return callback(err);
330 }
33169 if (playload === 'string') {
3323 return callback(null, data);
333 }
33466 callback(null, this.format_result(data, playload, args));
335 }, this);
336};
337
3381TBase.prototype.send_request = function (url, params, callback) {
33983 var args = {
340 type: 'GET',
341 playload: 'status',
342 headers: {}
343 };
34483 for (var k in params) {
345418 args[k] = params[k];
346 }
34783 args.type = (args.type || 'GET').toUpperCase();
34883 args.data = args.data || {};
349
35083 var user = args.user || args.data.user || {};
35183 args.user = user;
35283 if (args.data && args.data.user) {
3530 delete args.data.user;
354 }
355
35683 var api = args.api_host || this.config.host;
35783 if (args.api_host) {
3585 delete args.api_host;
359 }
360
36183 url = api + url.format(args.data);
362 // delete the url params
36383 url.replace(utils.STRING_FORMAT_REGEX, function (match, key) {
3640 delete args.data[key];
365 });
366
36783 if (args.playload !== 'string' && this.config.result_format) {
36842 url += this.config.result_format;
369 }
370
37183 this.apply_auth(url, args, user);
37283 if (args.type === 'POST') {
37334 args.headers['Content-Type'] = args.content_type || 'application/x-www-form-urlencoded;charset=UTF-8;';
374 }
37583 this.request(url, args, callback);
376};
377
378/**
379 * OAuth
380 */
381
3821TBase.prototype.format_authorization_url = function (params) {
3836 var login_url = (this.config.oauth_host || this.config.host) + this.config.oauth_authorize;
3846 return OAuth.addToURL(login_url, params);
385};
386
3871TBase.prototype.get_authorization_url = function (user, callback) {
3882 var self = this;
3892 self.get_request_token(user, function (err, token) {
3902 var info = null;
3912 if (err) {
3920 return callback(err);
393 }
3942 if (token) {
3952 var params = {
396 oauth_token: token.oauth_token,
397 oauth_callback: user.oauth_callback || self.config.oauth_callback
398 };
3992 info = token;
4002 info.blogtype = user.blogtype;
4012 info.auth_url = self.format_authorization_url(params);
402 }
4032 callback(err, info);
404 });
4052 return this;
406};
407
4081TBase.prototype.get_request_token = function (user, callback) {
4092 var self = this;
4102 var url = self.config.oauth_request_token;
4112 var params = {
412 type: 'GET',
413 user: user,
414 playload: 'string',
415 data: {
416 oauth_callback: user.oauth_callback || self.config.oauth_callback
417 },
418 api_host: self.config.oauth_host,
419 request_method: 'get_request_token'
420 };
4212 if (self.config.oauth_request_params) {
4220 utils.extend(params.data, self.config.oauth_request_params);
423 }
4242 self.send_request(url, params, function (err, token) {
4252 if (err) {
4260 return callback(err);
427 }
4282 token = self.format_access_token(token);
4292 token.blogtype = user.blogtype;
4302 callback(null, token);
431 });
4322 return this;
433},
434
435// user must contain oauth_pin or oauth_verifier
436TBase.prototype.get_access_token = function (user, callback) {
4371 if (!user.authtype) {
4381 user.authtype = 'oauth';
439 }
4401 var url = this.config.oauth_access_token;
4411 var data = {};
4421 var params = {
443 type: 'GET',
444 user: user,
445 playload: 'string',
446 data: data,
447 api_host: this.config.oauth_host,
448 request_method: 'get_access_token'
449 };
4501 var oauth_verifier = user.oauth_pin || user.oauth_verifier || 'no_verifier';
4511 if (oauth_verifier) {
4521 data.oauth_verifier = oauth_verifier;
4531 delete user.oauth_pin;
4541 delete user.oauth_verifier;
455 }
4561 if (user.authtype === 'xauth') {
4570 data.x_auth_username = user.username;
4580 data.x_auth_password = user.password;
4590 data.x_auth_mode = "client_auth";
460 }
4611 this.send_request(url, params, function (err, token) {
4621 if (err) {
4631 return callback(err);
464 }
4650 token = querystring.parse(token);
4660 token.blogtype = user.blogtype;
4670 callback(null, token);
468 });
4691 return this;
470};
471
4721TBase.prototype.apply_auth = function (url, args, user) {
47340 user.authtype = user.authtype || 'oauth';
47440 args.headers = args.headers || {};
47540 if (user.authtype === 'baseauth') {
4760 if (user.username && user.password) {
4770 args.headers.Authorization = urllib.make_base_auth_header(user.username, user.password);
478 }
47940 } else if (user.authtype === 'oauth' || user.authtype === 'xauth') {
48040 var accessor = {
481 consumerSecret: this.config.secret
482 };
483 // 已通过oauth认证
48440 if (user.oauth_token_secret) {
48538 accessor.tokenSecret = user.oauth_token_secret;
486 }
48740 var parameters = {};
488
48940 for (var k in args.data) {
490134 parameters[k] = args.data[k];
491134 if (k.substring(0, 6) === 'oauth_') { // 删除oauth_verifier相关参数
4923 delete args.data[k];
493 }
494 }
495
49640 var message = {
497 action: url,
498 method: args.type,
499 parameters: parameters
500 };
50140 message.parameters.oauth_consumer_key = this.config.appkey;
50240 message.parameters.oauth_version = '1.0';
503
504 // 已通过oauth认证
50540 if (user.oauth_token) {
50638 message.parameters.oauth_token = user.oauth_token;
507 }
508 // 设置时间戳
50940 OAuth.setTimestampAndNonce(message);
510 // 签名参数
511 // console.log(message.parameters);
51240 OAuth.SignatureMethod.sign(message, accessor);
513 // oauth参数通过get方式传递
51440 if (this.config.oauth_params_by_get) {
51540 args.data = message.parameters;
516 //console.log(args.data);
517 } else {
518 // 获取认证头部
5190 args.headers.Authorization = OAuth.getAuthorizationHeader(this.config.oauth_realm, message.parameters);
520 }
521 }
522};
523
524/**
525 * Result getters
526 */
527
5281TBase.prototype.get_result_items = function (data, playload, args) {
5290 throw new Error('Must override this method');
530};
531
5321TBase.prototype.get_result_item = function (data, playload, args) {
53331 return data;
534};
535
5361TBase.prototype.get_pagging_cursor = function (data, playload, args) {
53735 return {};
538};
539
540/**
541 * Result formaters
542 */
543
5441TBase.prototype.format_result = function (data, playload, args) {
545 // status[]: need Array and item type is `status`
546 // status: need item, type is `status`
54766 var index = playload.indexOf('[]');
54866 var isList = index > 0;
54966 if (isList) {
55035 var itemPlayload = playload.substring(0, index);
55135 var items = this.get_result_items(data, itemPlayload, args) || [];
55235 for (var i = 0; i < items.length; i++) {
553343 items[i] = this.format_result_item(items[i], itemPlayload, args);
554 }
55535 var result = {};
55635 result.items = items;
557 // try to get pagging cursor.
55835 result.cursor = this.get_pagging_cursor(data, itemPlayload, args);
55935 return result;
560 }
561
56231 var item = this.get_result_item(data, playload, args);
56331 return this.format_result_item(item, playload, args);
564};
565
5661TBase.prototype.format_result_item = function (data, playload, args) {
567374 var method = 'format_' + playload;
568374 return this[method](data, args);
569};
570
5711TBase.prototype.format_search_status = function (status, args) {
5720 throw new Error('Must override this method.');
573};
574
5751TBase.prototype.format_status = function (status, args) {
5760 throw new Error('Must override this method.');
577};
578
5791TBase.prototype.format_user = function (user, args) {
5800 throw new Error('Must override this method.');
581};
582
5831TBase.prototype.format_comment = function (comment, args) {
5840 throw new Error('Must override this method.');
585};
586
5871TBase.prototype.format_message = function (message, args) {
5880 throw new Error('Must override this method.');
589};
590
5911TBase.prototype.format_emotion = function (emotion, args) {
5920 throw new Error('Must override this method.');
593};
594
5951TBase.prototype.format_access_token = function (token) {
5962 return querystring.parse(token);
597};
598
5991TBase.prototype.format_count = function (count, args) {
6000 throw new Error('Must override this method.');
601};
602
6031TBase.prototype.format_favorite = function (favorite, args) {
6040 throw new Error('Must override this method.');
605};
606
607/**
608 * Params converters
609 */
610
6111TBase.prototype.convert_cursor = function (cursor) {
61219 return cursor;
613};
614
6151TBase.prototype.convert_status = function (status) {
6169 return status;
617};
618
6191TBase.prototype.convert_comment = function (comment) {
6208 return comment;
621};
622
6231TBase.prototype.convert_user = function (data) {
6244 return data;
625};
626
6271TBase.prototype.convert_ids = function (ids) {
6282 return {ids: ids};
629};
630
6311TBase.prototype.convert_favorite = function (id) {
6320 return {id: id};
633};
634
635/**
636 * User
637 */
638
6391TBase.prototype.verify_credentials = function (user, callback) {
6402 var params = {
641 type: 'GET',
642 user: user,
643 playload: 'user',
644 request_method: 'verify_credentials'
645 };
6462 var url = this.config.verify_credentials;
6472 this.send_request(url, params, callback);
6482 return this;
649};
650
6511TBase.prototype.user_show = function (user, uid, screen_name, callback) {
6525 var data = {};
6535 if (uid) {
6545 data.uid = uid;
655 }
6565 if (screen_name) {
6571 data.screen_name = screen_name;
6581 delete data.uid; // only support one
659 }
6605 data = this.convert_user(data);
6615 var params = {
662 type: 'GET',
663 user: user,
664 data: data,
665 playload: 'user',
666 request_method: 'user_show'
667 };
6685 var url = this.config.user_show;
6695 this.send_request(url, params, callback);
6705 return this;
671};
672
673/**
674 * Status APIs
675 */
676
6771TBase.prototype.update = function (user, status, callback) {
6788 status = this.convert_status(status);
6798 var params = {
680 type: 'POST',
681 playload: 'status',
682 user: user,
683 data: status,
684 request_method: 'update'
685 };
6868 var url = this.config.update;
6878 this.send_request(url, params, callback);
6888 return this;
689};
690
6911TBase.prototype.upload = function (user, status, pic, callback) {
6926 status = this.convert_status(status);
6936 pic.name = pic.name || 'node-weibo-upload-image.jpg';
6946 if (!pic.content_type) {
6956 pic.content_type = utils.mimeLookup(pic.name);
696 }
6976 if (Buffer.isBuffer(pic.data)) {
6980 this._upload(user, status, pic, callback);
6990 return this;
700 }
7016 var buffers = [];
7026 var size = 0;
7036 var self = this;
7046 pic.data.on('data', function (chunk) {
7056 size += chunk.length;
7066 buffers.push(chunk);
707 });
7086 pic.data.once('end', function () {
7096 pic.data = Buffer.concat(buffers, size);
7106 self._upload(user, status, pic, callback);
711 });
7126 pic.data.once('error', function (err) {
7130 callback(err);
714 });
7156 return this;
716};
717
7181TBase.prototype._upload = function (user, data, pic, callback) {
7196 var auth_args = {
720 type: 'post',
721 data: {},
722 headers: {}
723 };
7246 var pic_field = this.config.pic_field || 'pic';
7256 var boundary = 'boundary' + Date.now();
726 // this.format_upload_params(user, data, pic , boundary);
7276 var dashdash = '--';
7286 var crlf = '\r\n';
729
730 /* RFC2388 */
7316 var builder = '';
732
7336 builder += dashdash;
7346 builder += boundary;
7356 builder += crlf;
736
7376 var key;
7386 for (key in data) {
73910 var value = this.url_encode(data[key]);
74010 auth_args.data[key] = value;
741 }
742
7436 var api = this.config.host;
7446 var url = api + this.config.upload + this.config.result_format;
745
746 // 设置认证头部
7476 this.apply_auth(url, auth_args, user);
748
7496 for (key in auth_args.data) {
750 /* Generate headers. key */
75134 builder += 'Content-Disposition: form-data; name="' + key + '"';
75234 builder += crlf;
75334 builder += crlf;
754 /* Append form data. */
75534 builder += auth_args.data[key];
75634 builder += crlf;
757
758 /* Write boundary. */
75934 builder += dashdash;
76034 builder += boundary;
76134 builder += crlf;
762 }
763 /* Generate headers. [PIC] */
7646 builder += 'Content-Disposition: form-data; name="' + pic_field + '"';
765
7666 builder += '; filename="' + this.url_encode(pic.name) + '"';
7676 builder += crlf;
768
7696 builder += 'Content-Type: ' + pic.content_type + ';';
7706 builder += crlf;
7716 builder += crlf;
772
7736 var endstr = crlf + dashdash + boundary + dashdash + crlf;
7746 var builderLength = Buffer.byteLength(builder);
7756 var size = builderLength + pic.data.length + endstr.length;
7766 var buffer = new Buffer(size);
7776 var offset = 0;
7786 buffer.write(builder);
7796 offset += builderLength ;
7806 pic.data.copy(buffer, offset);
7816 offset += pic.data.length;
7826 buffer.write(endstr, offset);
783 // if (typeof BlobBuilder === 'undefined') {
784 // }
785 // else {
786 // buffer = new BlobBuilder(); //NOTE WebKitBlogBuilder
787 // buffer.append(builder);
788 // buffer.append(pic);
789 // buffer.append(endstr);
790 // buffer = buffer.getBlob();
791 // }
7926 auth_args.headers['Content-Type'] = 'multipart/form-data;boundary=' + boundary;
7936 var params = {
794 type: 'POST',
795 playload: 'status',
796 content: buffer,
797 headers: auth_args.headers,
798 request_method: 'upload'
799 };
8006 this.request(url, params, callback);
8016 return this;
802};
803
8041TBase.prototype.repost = function (user, id, status, callback) {
8054 status.id = id;
8064 status = this.convert_status(status);
8074 var params = {
808 type: 'POST',
809 playload: 'status',
810 user: user,
811 data: status,
812 request_method: 'repost'
813 };
8144 var url = this.config.repost;
8154 this.send_request(url, params, callback);
8164 return this;
817};
818
8191TBase.prototype.destroy = function (user, id, callback) {
8206 var params = {
821 type: 'POST',
822 playload: 'status',
823 user: user,
824 data: {id: id},
825 request_method: 'destroy'
826 };
8276 var url = this.config.destroy;
8286 this.send_request(url, params, callback);
8296 return this;
830};
831
8321TBase.prototype.show = function (user, id, callback) {
8332 var params = {
834 type: 'GET',
835 playload: 'status',
836 user: user,
837 data: {
838 id: id
839 },
840 request_method: 'show'
841 };
8422 var url = this.config.show;
8432 this.send_request(url, params, callback);
8442 return this;
845};
846
8471TBase.prototype.count = function (user, ids, callback) {
8484 var data = this.convert_ids(ids);
8494 var params = {
850 type: 'GET',
851 playload: 'count[]',
852 user: user,
853 data: data,
854 request_method: 'count'
855 };
8564 var url = this.config.count;
8574 this.send_request(url, params, function (err, result) {
8584 if (err) {
8592 return callback(err);
860 }
8612 callback(null, result.items);
862 });
8634 return this;
864};
865
8661TBase.prototype.home_timeline = function (user, cursor, callback) {
8674 return this._timeline('home_timeline', user, cursor, callback);
868};
869
8701TBase.prototype.user_timeline = function (user, cursor, callback) {
8714 return this._timeline('user_timeline', user, cursor, callback);
872};
873
8741TBase.prototype.public_timeline = function (user, cursor, callback) {
8754 return this._timeline('public_timeline', user, cursor, callback);
876};
877
8781TBase.prototype.mentions = function (user, cursor, callback) {
8794 return this._timeline('mentions', user, cursor, callback);
880};
881
8821TBase.prototype.repost_timeline = function (user, cursor, callback) {
8832 return this._timeline('repost_timeline', user, cursor, callback);
884};
885
8861TBase.prototype.search = function (user, query, cursor, callback) {
8871 cursor = cursor || {};
8881 cursor.count = cursor.count || 20;
8891 cursor = this.convert_cursor(cursor);
8901 query = utils.extend(query, cursor);
8911 var params = {
892 type: 'GET',
893 playload: 'status[]',
894 user: user,
895 data: query,
896 request_method: 'search'
897 };
8981 var url = this.config.search;
8991 this.send_request(url, params, callback);
9001 return this;
901};
902
903/**
904 * Favorite
905 */
906
9071TBase.prototype.favorites = function (user, cursor, callback) {
9082 return this._timeline('favorites', user, cursor, callback, 'favorite[]');
909};
910
9111TBase.prototype.favorite_request = function (type, name, user, id, callback) {
9120 var data = this.convert_favorite(id);
9130 var params = {
914 type: type,
915 playload: 'favorite',
916 user: user,
917 data: data,
918 request_method: name
919 };
9200 var url = this.config[name];
9210 this.send_request(url, params, callback);
9220 return this;
923};
924
9251TBase.prototype.favorite_show = function (user, id, callback) {
9260 return this.favorite_request('GET', 'favorite_show', user, id, callback);
927};
928
9291TBase.prototype.favorite_create = function (user, id, callback) {
9300 return this.favorite_request('POST', 'favorite_create', user, id, callback);
931};
932
9331TBase.prototype.favorite_destroy = function (user, id, callback) {
9340 return this.favorite_request('POST', 'favorite_destroy', user, id, callback);
935};
936
937/**
938 * Comment
939 */
940
9411TBase.prototype.comments = function (user, cursor, callback) {
9422 return this._timeline('comments', user, cursor, callback, 'comment[]');
943};
944
9451TBase.prototype.comments_timeline = function (user, cursor, callback) {
9464 return this._timeline('comments_timeline', user, cursor, callback, 'comment[]');
947};
948
9491TBase.prototype.comments_mentions = function (user, cursor, callback) {
9502 return this._timeline('comments_mentions', user, cursor, callback, 'comment[]');
951};
952
9531TBase.prototype.comments_to_me = function (user, cursor, callback) {
9542 return this._timeline('comments_to_me', user, cursor, callback, 'comment[]');
955};
956
9571TBase.prototype.comments_by_me = function (user, cursor, callback) {
9582 return this._timeline('comments_by_me', user, cursor, callback, 'comment[]');
959};
960
9611TBase.prototype.comment_create = function (user, id, comment, callback) {
9627 comment.id = id;
9637 comment = this.convert_comment(comment);
9647 var params = {
965 type: 'POST',
966 playload: 'comment',
967 user: user,
968 data: comment,
969 request_method: 'comment_create'
970 };
9717 var url = this.config.comment_create;
9727 this.send_request(url, params, callback);
9737 return this;
974};
975
9761TBase.prototype.comment_reply = function (user, cid, id, comment, callback) {
9776 comment.id = id;
9786 comment.cid = cid;
9796 comment = this.convert_comment(comment);
9806 var params = {
981 type: 'POST',
982 playload: 'comment',
983 user: user,
984 data: comment,
985 request_method: 'comment_reply'
986 };
9876 var url = this.config.comment_reply;
9886 this.send_request(url, params, callback);
9896 return this;
990};
991
9921TBase.prototype.comment_destroy = function (user, cid, callback) {
9931 var comment = {cid: cid};
9941 comment = this.convert_comment(comment);
9951 var params = {
996 type: 'POST',
997 playload: 'comment',
998 user: user,
999 data: comment,
1000 request_method: 'comment_destroy'
1001 };
10021 var url = this.config.comment_destroy;
10031 this.send_request(url, params, callback);
10041 return this;
1005};
1006

oauth.js

63%
221
140
81
LineHitsSource
1/*
2 * Copyright 2008 Netflix, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16// Here's some JavaScript software that's useful for implementing OAuth.
17// The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by
18// http://pajhome.org.uk/crypt/md5/sha1.js
19/* An OAuth message is represented as an object like this:
20 {method: "GET", action: "http://server.com/path", parameters: ...}
21 The parameters may be either a map {name: value, name2: value2}
22 or an Array of name-value pairs [[name, value], [name2, value2]].
23 The latter representation is more powerful: it supports parameters
24 in a specific sequence, or several parameters with the same name;
25 for example [["a", 1], ["b", 2], ["a", 3]].
26 Parameter names and values are NOT percent-encoded in an object.
27 They must be encoded before transmission and decoded after reception.
28 For example, this message object:
29 {method: "GET", action: "http://server/path", parameters: {p: "x y"}}
30 ... can be transmitted as an HTTP request that begins:
31 GET /path?p=x%20y HTTP/1.0
32 (This isn't a valid OAuth request, since it lacks a signature etc.)
33 Note that the object "x y" is transmitted as x%20y. To encode
34 parameters, you can call OAuth.addToURL, OAuth.formEncode or
35 OAuth.getAuthorization.
36 This message object model harmonizes with the browser object model for
37 input elements of an form, whose value property isn't percent encoded.
38 The browser encodes each value before transmitting it. For example,
39 see consumer.setInputs in example/consumer.js.
40 */
41
421(function () {
43
441var utils;
451if (typeof require !== 'undefined') {
461 utils = require('./utils');
47} else {
480 utils = weibo.utils;
49}
50
511var OAuth = {};
52
531OAuth.setProperties = function setProperties(into, from) {
543 if (into && from) {
553 for (var key in from) {
5624 into[key] = from[key];
57 }
58 }
593 return into;
60};
61
62// utility functions
631OAuth.setProperties(OAuth, {
64 percentEncode: function percentEncode(s) {
65980 if (!s) {
666 return "";
67 }
68974 if (s instanceof Array) {
690 var e = "";
700 for (var i = 0; i < s.length; ++s) {
710 if (e) {
720 e += '&';
73 }
740 e += percentEncode(s[i]);
75 }
760 return e;
77 }
78974 s = encodeURIComponent(s);
79 // Now replace the values which encodeURIComponent doesn't do
80 // encodeURIComponent ignores: - _ . ! ~ * ' ( )
81 // OAuth dictates the only ones you can ignore are: - _ . ~
82 // Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent
83974 s = s.replace(/\!/g, "%21");
84974 s = s.replace(/\*/g, "%2A");
85974 s = s.replace(/\'/g, "%27");
86 // s = s.replace("(", "%28", "g");
87974 s = s.replace(/\(/g, "%28");
88974 s = s.replace(/\)/g, "%29");
89974 return s;
90 },
91 decodePercent: decodeURIComponent,
92 /** Convert the given parameters to an Array of name-value pairs. */
93 getParameterList: function getParameterList(parameters) {
9486 if (!parameters) {
950 return [];
96 }
9786 if (typeof parameters !== "object") {
980 return this.decodeForm(parameters + "");
99 }
10086 if (parameters instanceof Array) {
10140 return parameters;
102 }
10346 var list = [];
10446 for (var p in parameters) {
105390 list.push([ p, parameters[p] ]);
106 }
10746 return list;
108 },
109 /** Convert the given parameters to a map from name to value. */
110 getParameterMap: function getParameterMap(parameters) {
111200 if (!parameters) {
1120 return {};
113 }
114200 if (typeof parameters !== "object") {
1150 return this.getParameterMap(this.decodeForm(parameters + ""));
116 }
117200 if (parameters instanceof Array) {
1180 var map = {};
1190 for (var p = 0; p < parameters.length; ++p) {
1200 var key = parameters[p][0];
1210 if (map[key] === undefined) { // first value wins
1220 map[key] = parameters[p][1];
123 }
124 }
1250 return map;
126 }
127200 return parameters;
128 },
129 formEncode: function formEncode(parameters) {
13046 var form = "";
13146 var list = OAuth.getParameterList(parameters);
13246 for (var p = 0, l = list.length; p < l; p++) {
133390 var pair = list[p];
134390 var value = pair[1];
135390 if (!value) {
1364 value = "";
137 }
138390 if (form) {
139344 form += '&';
140 }
141390 form += OAuth.percentEncode(pair[0]) + '=' + OAuth.percentEncode(value);
142 }
14346 return form;
144 },
145 decodeForm: function decodeForm(form) {
1460 var list = [];
1470 var nvps = form.split('&');
1480 for (var n = 0; n < nvps.length; ++n) {
1490 var nvp = nvps[n];
1500 if (!nvp) {
1510 continue;
152 }
1530 var equals = nvp.indexOf('=');
1540 var name;
1550 var value;
1560 if (equals < 0) {
1570 name = OAuth.decodePercent(nvp);
1580 value = null;
159 } else {
1600 name = OAuth.decodePercent(nvp.substring(0, equals));
1610 value = OAuth.decodePercent(nvp.substring(equals + 1));
162 }
1630 list.push([name, value]);
164 }
1650 return list;
166 },
167 setParameter: function setParameter(message, name, value) {
168160 var parameters = message.parameters;
169160 if (parameters instanceof Array) {
1700 for (var p = 0; p < parameters.length; ++p) {
1710 if (parameters[p][0] === name) {
1720 if (value === undefined) {
1730 parameters.splice(p, 1);
174 } else {
1750 parameters[p][1] = value;
1760 value = undefined;
177 }
178 }
179 }
1800 if (value !== undefined) {
1810 parameters.push([name, value]);
182 }
183 } else {
184160 parameters = OAuth.getParameterMap(parameters);
185160 parameters[name] = value;
186160 message.parameters = parameters;
187 }
188 },
189 setParameters: function setParameters(message, parameters) {
1900 var list = OAuth.getParameterList(parameters);
1910 for (var i = 0; i < list.length; ++i) {
1920 OAuth.setParameter(message, list[i][0], list[i][1]);
193 }
194 },
195 setTimestampAndNonce: function setTimestampAndNonce(message) {
19640 OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp());
19740 OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(32));
198 },
199 addToURL: function addToURL(url, parameters) {
2006 if (parameters) {
2016 var toAdd = OAuth.formEncode(parameters);
2026 if (toAdd) {
2036 if (url.indexOf('?') < 0) {
2046 url += '?';
205 } else {
2060 url += '&';
207 }
2086 url += toAdd;
209 }
210 }
2116 return url;
212 },
213 /** Construct the value of the Authorization header for an HTTP request. */
214 getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) {
2150 var header = '';
2160 if (realm) {
2170 header += ', realm="' + OAuth.percentEncode(realm) + '"';
218 }
2190 var list = OAuth.getParameterList(parameters);
2200 for (var p = 0; p < list.length; ++p) {
2210 var parameter = list[p];
2220 var name = parameter[0];
2230 if (name.indexOf("oauth_") === 0) {
2240 header += ', ' + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"';
225 }
226 }
2270 return 'OAuth ' + header.substring(2);
228 },
229
230 timestamp: function timestamp() {
23141 return Math.floor(new Date().getTime() / 1000);
232 },
233
234 nonce: function nonce(length) {
235141 if (!length) {
2362 return '';
237 }
238139 var chars = OAuth.nonce.CHARS;
239139 var result = "";
240139 for (var i = 0; i < length; ++i) {
2416230 var rnum = Math.floor(Math.random() * chars.length);
2426230 result += chars.substring(rnum, rnum + 1);
243 }
244139 return result;
245 }
246});
247
2481OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
249/** Define a constructor function,
250 without causing trouble to anyone who was using it as a namespace.
251 That is, if parent[name] already existed and had properties,
252 copy those properties into the new constructor.
253 */
2541OAuth.declareClass = function declareClass(parent, name, newConstructor) {
2551 var previous = parent[name];
2561 parent[name] = newConstructor;
2571 if (newConstructor && previous) {
2580 for (var key in previous) {
2590 if (key !== "prototype") {
2600 newConstructor[key] = previous[key];
261 }
262 }
263 }
2641 return newConstructor;
265};
266
267/** An abstract algorithm for signing messages. */
2681OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod() {});
269
270// instance members
2711OAuth.setProperties(OAuth.SignatureMethod.prototype, {
272 /** Add a signature to the message. */
273 sign: function sign(message) {
27440 var baseString = OAuth.SignatureMethod.getBaseString(message);
27540 var signature = this.getSignature(baseString);
276 // console.log(baseString, this.key, signature)
27740 OAuth.setParameter(message, "oauth_signature", signature);
27840 return signature; // just in case someone's interested
279 },
280 /** Set the key string for signing. */
281 initialize: function initialize(name, accessor) {
28240 var consumerSecret;
28340 if (accessor.accessorSecret && name.length > 9 && name.substring(name.length-9) === "-Accessor") {
2840 consumerSecret = accessor.accessorSecret;
285 } else {
28640 consumerSecret = accessor.consumerSecret;
287 }
28840 this.key = OAuth.percentEncode(consumerSecret) + "&" + OAuth.percentEncode(accessor.tokenSecret);
289 }
290});
291
292/* SignatureMethod expects an accessor object to be like this:
293 {tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."}
294 The accessorSecret property is optional.
295 */
296// Class members:
2971OAuth.setProperties(OAuth.SignatureMethod, {
298 sign: function sign(message, accessor) {
29940 var name = OAuth.getParameterMap(message.parameters).oauth_signature_method;
30040 if (!name) {
30140 name = 'HMAC-SHA1';
30240 OAuth.setParameter(message, 'oauth_signature_method', name);
303 }
30440 OAuth.SignatureMethod.newMethod(name, accessor).sign(message);
305 },
306
307 /** Instantiate a SignatureMethod for the given method name. */
308 newMethod: function newMethod(name, accessor) {
30940 var Impl = OAuth.SignatureMethod.REGISTERED[name];
31040 if (typeof Impl === 'function') {
31140 var method = new Impl();
31240 method.initialize(name, accessor);
31340 return method;
314 }
3150 var err = new Error("signature_method_rejected");
3160 var acceptable = "";
3170 for (var r in OAuth.SignatureMethod.REGISTERED) {
3180 if (acceptable) {
3190 acceptable += '&';
320 }
3210 acceptable += OAuth.percentEncode(r);
322 }
3230 err.oauth_acceptable_signature_methods = acceptable;
3240 throw err;
325 },
326 /** A map from signature method name to constructor. */
327 REGISTERED: {},
328 /** Subsequently, the given constructor will be used for the named methods.
329 The constructor will be called with no parameters.
330 The resulting object should usually implement getSignature(baseString).
331 You can easily define such a constructor by calling makeSubclass, below.
332 */
333 registerMethodClass: function registerMethodClass(names, classConstructor) {
3342 for (var n = 0, l = names.length; n < l; ++n) {
3354 OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor;
336 }
337 },
338 /** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */
339 makeSubclass: function makeSubclass(getSignatureFunction) {
3402 var SuperClass = OAuth.SignatureMethod;
3412 var subClass = function() {
34240 SuperClass.call(this);
343 };
3442 subClass.prototype = new SuperClass();
345 // Delete instance variables from prototype:
346 // delete subclass.prototype... There aren't any.
3472 subClass.prototype.getSignature = getSignatureFunction;
3482 subClass.prototype.constructor = subClass;
3492 return subClass;
350 },
351 getBaseString: function getBaseString(message) {
35240 var URL = message.action;
35340 var q = URL.indexOf('?');
35440 var parameters;
35540 if (q < 0) {
35640 parameters = message.parameters;
357 } else {
358 // Combine the URL query string with the other parameters:
3590 parameters = OAuth.decodeForm(URL.substring(q + 1));
3600 var toAdd = OAuth.getParameterList(message.parameters);
3610 for (var a = 0, l = toAdd.length; a < l; ++a) {
3620 parameters.push(toAdd[a]);
363 }
364 }
36540 return OAuth.percentEncode(message.method.toUpperCase()) + '&' +
366 OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL)) + '&' +
367 OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters));
368 },
369 normalizeUrl: function normalizeUrl(url) {
37040 var uri = OAuth.SignatureMethod.parseUri(url);
37140 var scheme = uri.protocol.toLowerCase();
37240 var authority = uri.authority.toLowerCase();
37340 var dropPort = (scheme === "http" && uri.port === 80) || (scheme === "https" && uri.port === 443);
37440 if (dropPort) {
375 // find the last : in the authority
3760 var index = authority.lastIndexOf(":");
3770 if (index >= 0) {
3780 authority = authority.substring(0, index);
379 }
380 }
38140 var path = uri.path;
382// if (!path) {
383// path = "/"; // conforms to RFC 2616 section 3.2.2
384// }
385 // we know that there is no query and no fragment here.
38640 return scheme + "://" + authority + path;
387 },
388 parseUri: function parseUri(str) {
389 /* This function was adapted from parseUri 1.2.1
390 http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
391 */
39240 var o = {
393 key: [ "source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
394 parser: { strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }};
39540 var m = o.parser.strict.exec(str);
39640 var uri = {};
39740 var i = 14;
39840 while (i--) {
399560 uri[o.key[i]] = m[i] || "";
400 }
40140 return uri;
402 },
403 normalizeParameters: function normalizeParameters(parameters) {
40440 if (!parameters) {
4050 return "";
406 }
40740 var norm = [];
40840 var list = OAuth.getParameterList(parameters);
40940 for (var p = 0; p < list.length; ++p) {
410372 var nvp = list[p];
411372 if (nvp[0] !== "oauth_signature") {
412372 norm.push(nvp);
413 }
414 }
41540 norm.sort(function (a, b) {
4161225 if (a[0] < b[0]) { return -1; }
4171274 if (a[0] > b[0]) { return 1; }
4180 if (a[1] < b[1]) { return -1; }
4190 if (a[1] > b[1]) { return 1; }
4200 return 0;
421 });
42240 return OAuth.formEncode(norm);
423 }
424});
425
4261OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"],
427OAuth.SignatureMethod.makeSubclass(
428 function getSignature(baseString) {
4290 return this.key;
430 }
431));
432
4331OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"],
434OAuth.SignatureMethod.makeSubclass(
435 function getSignature(baseString) {
43640 return utils.base64HmacSha1(baseString, this.key);
437 }
438));
439
4401var root = this; // window on browser
4411if (typeof module === 'undefined') {
4420 root.weibo = root.weibo || {};
4430 root.weibo.OAuth = OAuth;
444} else {
4451 module.exports = OAuth;
446}
447
448})();

weibo_util.js

56%
41
23
18
LineHitsSource
1/**
2 * 新浪微博mid与url互转实用工具
3 * 作者: XiNGRZ (http://weibo.com/xingrz)
4 */
5
61var WeiboUtil = module.exports = {
7 // 62进制字典
8 str62keys: [
9 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
10 "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
11 "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
12 ],
13};
14
15/**
16 * 62进制值转换为10进制
17 * @param {String} str62 62进制值
18 * @return {String} 10进制值
19 */
201WeiboUtil.str62to10 = function (str62) {
210 var i10 = 0;
220 for (var i = 0; i < str62.length; i++) {
230 var n = str62.length - i - 1;
240 var s = str62[i];
250 i10 += this.str62keys.indexOf(s) * Math.pow(62, n);
26 }
270 return i10;
28};
29
30/**
31 * 10进制值转换为62进制
32 * @param {String} int10 10进制值
33 * @return {String} 62进制值
34 */
351WeiboUtil.int10to62 = function (int10) {
36735 var s62 = '';
37735 var r = 0;
38735 while (int10 !== 0 && s62.length < 100) {
392319 r = int10 % 62;
402319 s62 = this.str62keys[r] + s62;
412319 int10 = Math.floor(int10 / 62);
42 }
43735 return s62;
44};
45
46/**
47 * URL字符转换为mid
48 * @param {String} url 微博URL字符,如 "wr4mOFqpbO"
49 * @return {String} 微博mid,如 "201110410216293360"
50 */
511WeiboUtil.url2mid = function (url) {
520 var mid = '';
53 //从最后往前以4字节为一组读取URL字符
540 for (var i = url.length - 4; i > -4; i = i - 4) {
550 var offset1 = i < 0 ? 0 : i;
560 var offset2 = i + 4;
570 var str = url.substring(offset1, offset2);
58
590 str = this.str62to10(str);
600 if (offset1 > 0) {
61 //若不是第一组,则不足7位补0
620 while (str.length < 7) {
630 str = '0' + str;
64 }
65 }
66
670 mid = str + mid;
68 }
69
700 return mid;
71};
72
73/**
74 * mid转换为URL字符
75 * @param {String} mid 微博mid,如 "201110410216293360"
76 * @return {String} 微博URL字符,如 "wr4mOFqpbO"
77 */
781WeiboUtil.mid2url = function (mid) {
79264 if (!mid) {
800 return mid;
81 }
82264 mid = String(mid); //mid数值较大,必须为字符串!
83264 if (!/^\d+$/.test(mid)) { return mid; }
84264 var url = '';
85 // 从最后往前以7字节为一组读取mid
86264 for (var i = mid.length - 7; i > -7; i = i - 7) {
87735 var offset1 = i < 0 ? 0 : i;
88735 var offset2 = i + 7;
89735 var num = mid.substring(offset1, offset2);
90
91735 num = this.int10to62(num);
92735 url = num + url;
93 }
94264 return url;
95};

tqq.js

93%
189
176
13
LineHitsSource
1/*!
2 * node-weibo - lib/tqq.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var inherits = require('util').inherits;
141var TBase = require('./tbase');
151var utils = require('./utils');
16
171function TQQAPI(options) {
181 TQQAPI.super_.call(this);
19
201 var config = utils.extend({}, options, {
21 host: 'http://open.t.qq.com/api',
22 result_format: '',
23 oauth_host: 'https://open.t.qq.com',
24 oauth_authorize: '/cgi-bin/authorize',
25 oauth_request_token: '/cgi-bin/request_token',
26 oauth_access_token: '/cgi-bin/access_token',
27
28 // 竟然是通过get传递
29 oauth_params_by_get: true,
30 support_comment: false, // 不支持comment_timeline
31 support_do_comment: true,
32 support_repost_timeline: true, // 支持查看转发列表
33 support_favorites_max_id: true,
34 reply_dont_need_at_screen_name: true, // @回复某条微博 无需填充@screen_name
35 rt_at_name: true, // RT的@name而不是@screen_name
36 repost_delimiter: ' || ', //转发时的分隔符
37 support_counts: false, // 只有rt_count这个,不过貌似有问题,总是404。暂时隐藏
38
39 home_timeline: '/statuses/home_timeline',
40 mentions: '/statuses/mentions_timeline',
41 comments_timeline: '/statuses/mentions_timeline',
42 comments_mentions: '/statuses/mentions_timeline',
43
44 repost_timeline: '/t/re_list',
45
46 followers: '/friends/user_fanslist',
47 friends: '/friends/user_idollist',
48 favorites: '/fav/list_t',
49 favorites_create: '/fav/addt',
50 favorites_destroy: '/fav/delt',
51 count: '/t/re_count', //仅仅是转播数
52 show: '/t/show',
53 update: '/t/add',
54 upload: '/t/add_pic',
55 repost: '/t/re_add',
56 comment_create: '/t/comment',
57 comment_reply: '/t/comment',
58 comments: '/t/re_list',
59 destroy: '/t/del',
60 destroy_msg: '/private/del',
61 direct_messages: '/private/recv',
62 sent_direct_messages: '/private/send',
63 new_message: '/private/add',
64 rate_limit_status: '/account/rate_limit_status',
65 friendships_create: '/friends/add',
66 friendships_destroy: '/friends/del',
67 friendships_show: '/friends/check',
68 reset_count: '/statuses/reset_count',
69 user_show: '/user/other_info',
70
71 // 用户标签
72 tags: '/tags',
73 create_tag: '/tags/create',
74 destroy_tag: '/tags/destroy',
75 tags_suggestions: '/tags/suggestions',
76
77 // 搜索
78 search: '/search/t',
79 user_search: '/search/user',
80 verify_credentials: '/user/info',
81
82 gender_map: {0: 'n', 1: 'm', 2: 'f'},
83
84 // support apis
85 support_comment_destroy: false,
86 support_comments_mentions: false,
87 support_comments_to_me: false,
88 support_comments_by_me: false,
89 });
90
911 this.init(config);
92}
93
941inherits(TQQAPI, TBase);
951module.exports = TQQAPI;
96
97/**
98 * Utils methods
99 */
100
1011TQQAPI.prototype.detect_error = function (method, res, playload, data) {
10240 var headers = res.headers;
10340 var err;
10440 if (res.statusCode === 200 && headers.status) {
1051 err = new Error(headers.status);
10639 } else if (data.errcode && data.msg) {
1079 err = new Error(data.msg);
10830 } else if (!data.data && data.msg && data.msg !== 'ok') {
1090 err = new Error(data.msg);
110 }
11140 if (err) {
11210 err.name = this.errorname(method);
11310 err.data = data;
11410 return err;
115 }
11630 return TQQAPI.super_.prototype.detect_error.call(this, method, res, playload, data);
117};
118
1191TQQAPI.prototype.url_encode = function (text) {
1208 return text;
121};
122
123/**
124 * Result getters
125 */
126
1271TQQAPI.prototype.get_result_items = function (data, playload, args) {
12815 if (playload === 'count') {
1291 var counts = [];
1301 for (var id in data) {
1312 var item = data[id];
1322 counts.push({id: id, reposts: item.count, comments: item.mcount});
133 }
1341 return counts;
135 }
13614 return data.info;
137};
138
139/**
140 * { hasnext: 0,
141 info:
142 [ [Object],
143 [Object],
144 [Object],
145 [Object],
146 [Object],
147 [Object],
148 [Object],
149 [Object],
150 [Object] ],
151 timestamp: 1348753615,
152 user: { GreenMango: '青芒', 'node-weibo': 'node-weibo' } },
153 */
154// TQQAPI.prototype.get_pagging_cursor = function (data, playload, args) {
155// return {};
156// };
157
158/**
159 * Result formatters
160 */
161
1621TQQAPI.prototype.format_result = function (data, playload, args) {
16328 data = data.data;
16428 var result = TQQAPI.super_.prototype.format_result.call(this, data, playload, args);
16528 if (data.user) {
16615 result.users = data.user;
167 }
16828 return result;
169};
170
1711TQQAPI.prototype.format_search_status = function (status, args) {
1720 throw new Error('Must override this method.');
173};
174
175/**
176 *
177{ city_code: '1',
178 count: 0,
179 country_code: '1',
180 emotiontype: 0,
181 emotionurl: '',
182 from: '微博开放平台',
183 fromurl: 'http://wiki.open.t.qq.com/index.php/%E4%BA%A7%E5%93%81%E7%B1%BBFAQ#.E6.8F.90.E4.BA.A4.E5.BA.94.E7.94.A8.E6.9D.A5.E6.BA.90.E5.AD.97.E6.AE.B5.E5.AE.A1.E6.A0.B8.E8.83.BD.E5.BE.97.E5.88.B0.E4.BB.80.E4.B9.88.E5.A5.BD.E5.A4.84.EF.BC.9F\n',
184 geo: '广东省中山市康乐路10号',
185 head: 'http://app.qlogo.cn/mbloghead/cb1c4eb21aa2b52a233a',
186 id: '102460077174373',
187 image: null,
188 isrealname: 2,
189 isvip: 0,
190 jing: '113.421234',
191 latitude: '22.354231',
192 location: '中国 浙江 杭州',
193 longitude: '113.421234',
194 mcount: 0,
195 music: null,
196 name: 'node-weibo',
197 nick: 'node-weibo',
198 openid: 'EA68676D5E9DA465822CD0CEB2DC6EF5',
199 origtext: '这是update(user, status, callback) 的单元测试,当前时间 Thu Sep 27 2012 17:04:25 GMT+0800 (CST)',
200 province_code: '33',
201 self: 1,
202 source: null,
203 status: 0,
204 text: '这是update(user, status, callback) 的单元测试,当前时间 Thu Sep 27 2012 17:04:25 GMT+0800 (CST)',
205 timestamp: 1348736665,
206 type: 1,
207 user: { 'node-weibo': 'node-weibo' },
208 video: null,
209 wei: '22.354231' }
210
211 * @param {[type]} status [description]
212 * @param {[type]} args [description]
213 * @return {[type]} [description]
214 */
2151TQQAPI.prototype.format_status = function (data, args) {
216187 var status = {};
217187 status.id = String(data.id);
218187 status.t_url = 'http://t.qq.com/p/t/' + data.id;
219187 status.created_at = new Date(data.timestamp * 1000);
220187 status.text = data.origtext;
221187 status.source = '<a href="' + data.fromurl + '">' + data.from + '</a>';
222 // status.favorited =
223187 if (data.image && data.image[0]) {
22482 var image = data.image[0];
22582 status.thumbnail_pic = image + '/160';
22682 status.bmiddle_pic = image + '/460';
22782 status.original_pic = image + '/2000';
228 }
229187 if (data.latitude && String(data.latitude) !== '0') {
23017 status.geo = this.format_geo(data, args);
231 }
232187 if (data.name) {
233175 status.user = this.format_user(data, args);
234 }
235187 status.reposts_count = data.count || 0;
236187 status.comments_count = data.mcount || 0;
237187 if (data.source) {
23833 status.retweeted_status = this.format_status(data.source, args);
239 }
240187 return status;
241 // type:微博类型 1-原创发表、2-转载、3-私信 4-回复 5-空回 6-提及 7: 点评
242// status.status_type = data.type;
243// if(data.type == 7) {
244// // 腾讯的点评会今日hometimeline,很不给力
245// status.status_type = 'comments_timeline';
246// }
247
248// status.created_at = new Date(data.timestamp * 1000);
249// status.timestamp = data.timestamp;
250// if(data.image){
251// status.thumbnail_pic = data.image[0] + '/160';
252// status.bmiddle_pic = data.image[0] + '/460';
253// status.original_pic = data.image[0] + '/2000';
254// }
255// if (data.source) {
256// if(data.type == 4) {
257// // 回复
258// status.text = '@' + data.source.name + ' ' + status.text;
259// status.related_dialogue_url = 'http://t.qq.com/p/r/' + status.id;
260// status.in_reply_to_status_id = data.source.id;
261// status.in_reply_to_screen_name = data.source.nick;
262// } else {
263// status.retweeted_status =
264// this.format_result_item(data.source, 'status', args, users);
265// // 评论
266// if(play_load == 'comment') {
267// status.status = status.retweeted_status;
268// delete status.retweeted_status;
269// }
270// }
271// }
272// status.repost_count = data.count || 0;
273// status.comments_count = data.mcount || 0; // 评论数
274// status.source = data.from;
275// status.user = this.format_result_item(data, 'user', args, users);
276// // 收件人
277// // tohead: ""
278// // toisvip: 0
279// // toname: "macgirl"
280// // tonick: "美仪"
281// if(data.toname) {
282// status.recipient = {
283// name: data.toname,
284// nick: data.tonick,
285// isvip: data.toisvip,
286// head: data.tohead
287// };
288// status.recipient = this.format_result_item(status.recipient, 'user', args, users);
289// }
290
291// // 如果有text属性,则替换其中的@xxx 为 中文名(@xxx)
292// if(status && status.text) {
293// var matchs = status.text.match(this.ONLY_AT_USER_RE);
294// if(matchs) {
295// status.users = {};
296// for(var j=0; j<matchs.length; j++) {
297// var name = matchs[j].trim().substring(1);
298// status.users[name] = users[name];
299// }
300// }
301// }
302// data = status;
303};
304
305/**
306 *
307{ birth_day: 1,
308 birth_month: 1,
309 birth_year: 2010,
310 city_code: '1',
311 comp: null,
312 country_code: '1',
313 edu: null,
314 email: '',
315 exp: 56,
316 fansnum: 3,
317 favnum: 0,
318 head: 'http://app.qlogo.cn/mbloghead/2045de7c75623f2c2b06',
319 homecity_code: '',
320 homecountry_code: '',
321 homepage: '',
322 homeprovince_code: '',
323 hometown_code: '',
324 idolnum: 46,
325 industry_code: 0,
326 introduction: '',
327 isent: 0,
328 ismyblack: 0,
329 ismyfans: 0,
330 ismyidol: 0,
331 isrealname: 2,
332 isvip: 0,
333 level: 1,
334 location: '中国 杭州',
335 mutual_fans_num: 0,
336 name: 'node-weibo',
337 nick: 'node-weibo',
338 openid: 'EA68676D5E9DA465822CD0CEB2DC6EF5',
339 province_code: '33',
340 regtime: 1348724066,
341 send_private_flag: 2,
342 sex: 1,
343 tag: null,
344 tweetinfo:
345 [ { city_code: '1',
346 country_code: '1',
347 emotiontype: 0,
348 emotionurl: '',
349 from: '腾讯微博',
350 fromurl: 'http://t.qq.com\n',
351 geo: '',
352 id: '70997003338788',
353 image: null,
354 latitude: '0',
355 location: '中国 杭州',
356 longitude: '0',
357 music: null,
358 origtext: '#新人报到# 伟大的旅程都是从第一条微博开始的!',
359 province_code: '33',
360 self: 1,
361 status: 0,
362 text: '#新人报到# 伟大的旅程都是从第一条微博开始的!',
363 timestamp: 1348724111,
364 type: 1,
365 video: null } ],
366 tweetnum: 1,
367 verifyinfo: '' }
368 */
3691TQQAPI.prototype.format_user = function (data, args) {
370177 var user = {};
371177 user.id = data.name;
372177 user.t_url = 'http://t.qq.com/' + data.name;
373177 user.screen_name = data.nick;
374177 user.name = data.name;
375177 user.location = data.location || '';
376177 user.description = data.introduction || '';
377 // no url
378177 if (data.head) {
379173 user.profile_image_url = data.head + '/50'; // 竟然直接获取的地址无法拿到头像
380173 user.avatar_large = data.head + '/180';
381 } else {
3824 user.profile_image_url = 'http://mat1.gtimg.com/www/mb/images/head_50.jpg';
3834 user.avatar_large = 'http://mat1.gtimg.com/www/mb/images/head_180.jpg';
384 }
385177 user.gender = this.config.gender_map[data.sex||0];
386177 user.followers_count = data.fansnum || 0;
387177 user.friends_count = data.idolnum || 0;
388177 user.statuses_count = data.tweetnum || 0;
389177 user.favourites_count = data.favnum || 0;
390177 if (data.regtime) {
3912 user.created_at = new Date(data.regtime * 1000);
392 }
393177 user.following = data.ismyidol || false;
394177 user.follow_me = data.ismyfans || false;
395 // send_private_flag : 是否允许所有人给当前用户发私信,0-仅有偶像,1-名人+听众,2-所有人,
396177 user.allow_all_act_msg = data.send_private_flag === 2;
397 // no geo_enabled
398177 user.verified = !!data.isvip;
399 // no verified_type
400177 user.verified_reason = data.verifyinfo || '';
401 // user.remark =
402177 user.allow_all_comment = true;
403 // user.online_status = true;
404177 user.bi_followers_count = data.mutual_fans_num || 0;
405 // user.lang
406177 if (data.tweetinfo && data.tweetinfo[0]) {
4072 user.status = this.format_status(data.tweetinfo[0], args);
408 }
409
410177 if (data.tag) {
4111 user.tags = data.tag;
412 }
413177 return user;
414};
415
4161TQQAPI.prototype.format_count = function (count, args) {
4172 return count;
418};
419
4201TQQAPI.prototype.format_geo = function (data, args) {
42117 var geo = {
422 longitude: data.longitude,
423 latitude: data.latitude,
424 // city_name string City name "广州"
425 // province_name string Province name "广东"
426 address: data.geo,
427 };
42817 return geo;
429};
430
4311TQQAPI.prototype.format_comment = function (data, args) {
43217 var comment = this.format_status(data, args);
43317 if (comment.retweeted_status) {
43415 comment.status = comment.retweeted_status;
43515 delete comment.retweeted_status;
436 }
43717 return comment;
438};
439
4401TQQAPI.prototype.format_message = function (message, args) {
4410 throw new Error('Must override this method.');
442};
443
4441TQQAPI.prototype.format_emotion = function (emotion, args) {
4450 throw new Error('Must override this method.');
446};
447
4481TQQAPI.prototype.format_favorite = function (status, args) {
44919 var favorite = {
450 created_at: new Date(status.storetime * 1000),
451 status: this.format_status(status)
452 };
45319 return favorite;
454};
455
456/**
457 * Params converters
458 */
459
4601TQQAPI.prototype.convert_comment = function (comment) {
461 // http://wiki.open.t.qq.com/index.php/%E5%BE%AE%E5%8D%9A%E7%9B%B8%E5%85%B3/%E7%82%B9%E8%AF%84%E4%B8%80%E6%9D%A1%E5%BE%AE%E5%8D%9A
4626 var data = {
463 content: comment.comment,
464 reid: comment.id
465 };
4666 return data;
467};
468
4691TQQAPI.prototype.convert_status = function (status) {
470 // syncflag 微博同步到空间分享标记(可选,0-同步,1-不同步,默认为0),目前仅支持oauth1.0鉴权方式
4719 var data = {
472 content: status.status
473 };
4749 if (status.long) {
4754 data.longitude = status.long;
4764 data.latitude = status.lat;
477 }
4789 if (status.id) {
4792 data.reid = status.id;
480 }
4819 return data;
482};
483
4841TQQAPI.prototype.convert_user = function (user) {
4851 var data = {
486 name: user.uid || user.screen_name
487 };
4881 return data;
489};
490
4911TQQAPI.prototype.convert_ids = function (ids) {
4922 return {
493 ids: ids,
494 flag: '2'
495 };
496};
497
498/**
499 * pageflag
500 分页标识(0:第一页,1:向下翻页,2:向上翻页)
501 pagetime
502 本页起始时间(第一页:填0,向上翻页:填上一次请求返回的第一条记录时间,向下翻页:填上一次请求返回的最后一条记录时间)
503 reqnum
504 每次请求记录的条数(1-70条)
505 type
506 拉取类型(需填写十进制数字)
5070x1 原创发表 0x2 转载 如需拉取多个类型请使用|,如(0x1|0x2)得到3,则type=3即可,填零表示拉取所有类型
508 contenttype
509 内容过滤。0-表示所有类型,1-带文本,2-带链接,4-带图片,8-带视频,0x10-带音频
510 建议不使用contenttype为1的类型,如果要拉取只有文本的微博,建议使用0x80
511 *
512 */
5131TQQAPI.prototype.convert_cursor = function (cursor) {
51414 var data = {};
515 // type: 拉取类型, 0x1 原创发表 0x2 转载 0x8 回复 0x10 空回 0x20 提及 0x40 点评
51614 data.type = String(0x1 | 0x2 | 0x8 | 0x10 | 0x20);
51714 data.contenttype = '0';
51814 data.reqnum = cursor.count;
51914 if (cursor.max_id) {
520 // get older statuses
5210 data.pageflag = '1';
5220 data.pagetime = cursor.max_time;
5230 data.lastid = cursor.max_id;
52414 } else if (cursor.since_id) {
525 // get newer statuses
526 // 0:第一页,1:向下翻页,2:向上翻页
5270 data.pageflag = '2';
5280 data.pagetime = cursor.since_time;
529 // data.lastid = cursor.sina_id;
530 } else {
531 // top page
53214 data.pageflag = '0';
53314 data.pagetime = '0';
53414 data.lastid = '0';
535 }
53614 if (typeof cursor.callback === 'function') {
5377 data = cursor.callback(data);
538 }
53914 return data;
540};
541
542/**
543 * Status
544 */
545
5461TQQAPI.prototype.repost_timeline = function (user, cursor, callback) {
5471 cursor.callback = function (data) {
5481 data.rootid = cursor.id;
5491 data.flag = '0';
550 // twitterid 微博id,与pageflag、pagetime共同使用,实现翻页功能(第1页填0,继续向下翻页,填上一次请求返回的最后一条记录id)
5511 if (data.lastid) {
5521 data.twitterid = data.lastid;
5531 delete data.lastid;
554 }
5551 return data;
556 };
5571 return TQQAPI.super_.prototype.repost_timeline.call(this, user, cursor, callback);
558};
559
5601TQQAPI.prototype.user_timeline = function (user, cursor, callback) {
5612 cursor.callback = function (data) {
5622 if (cursor.uid || cursor.screen_name) {
5631 data.name = cursor.uid || cursor.screen_name;
564 }
5652 return data;
566 };
5672 return TQQAPI.super_.prototype.user_timeline.call(this, user, cursor, callback);
568};
569
5701TQQAPI.prototype.search = function (user, query, cursor, callback) {
5711 cursor = cursor || {};
5721 var q = {
573 keyword: query.q
574 };
5751 if (query.long && query.lat && query.radius) {
5760 q.longitude = query.long;
5770 q.latitude = query.lat;
5780 q.radius = query.radius;
579 }
5801 cursor.callback = function (data) {
5811 data.pagesize = data.reqnum || 20;
5821 return data;
583 };
5841 return TQQAPI.super_.prototype.search.call(this, user, q, cursor, callback);
585};
586
587/**
588 * Comment
589 */
590
5911TQQAPI.prototype.comments_timeline = function (user, cursor, callback) {
5922 cursor.callback = function (data) {
5932 data.type = String(0x40);
5942 return data;
595 };
5962 return TQQAPI.super_.prototype.comments_timeline.call(this, user, cursor, callback);
597};
598
5991TQQAPI.prototype.comments = function (user, cursor, callback) {
6001 cursor.callback = function (data) {
6011 data.rootid = cursor.id;
6021 data.flag = '1';
6031 if (data.lastid) {
6041 data.twitterid = data.lastid;
6051 delete data.lastid;
606 }
6071 return data;
608 };
6091 return TQQAPI.super_.prototype.comments.call(this, user, cursor, callback);
610};
611
6121TQQAPI.prototype.comment_destroy = function (user, cid, callback) {
6130 callback(new TypeError('comment_destroy not support.'));
614};
615

weibo.js

95%
63
60
3
LineHitsSource
1/*!
2 * node-weibo - lib/weibo.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var TBaseOauthV2 = require('./tbase_oauth_v2');
141var inherits = require('util').inherits;
151var utils = require('./utils');
161var weiboutil = require('./weibo_util');
17
18
191function WeiboAPI(options) {
202 WeiboAPI.super_.call(this);
21
222 var config = utils.extend({}, options, {
23 host: 'https://api.weibo.com/2',
24 user_home_url: 'http://weibo.com/n/',
25 search_url: 'http://s.weibo.com/weibo/',
26
27 oauth_host: 'https://api.weibo.com/oauth2',
28 oauth_authorize: '/authorize',
29 oauth_access_token: '/access_token',
30 verify_credentials: '/users/show',
31
32 comments: '/comments/show',
33 comment_create: '/comments/create',
34 comment_reply: '/comments/reply',
35 comment_destroy: '/comments/destroy',
36
37 support_search: false,
38 });
39
402 this.init(config);
41}
42
431inherits(WeiboAPI, TBaseOauthV2);
441module.exports = WeiboAPI;
45
46/**
47 * Result getters
48 */
49
501WeiboAPI.prototype.get_result_items = function (data, playload, args) {
5120 return data.statuses || data.comments || data.reposts ||
52 data.messages || data.favorites || data;
53};
54
55/**
56 * Result formatters
57 */
58
591WeiboAPI.prototype.format_search_status = function (status, args) {
600 return status;
61};
62
631WeiboAPI.prototype.format_status = function (status, args) {
64266 status.id = status.idstr;
65266 status.created_at = new Date(status.created_at);
66266 if (status.user) {
67258 status.user = this.format_user(status.user, args);
68258 status.t_url = 'http://weibo.com/' + status.user.id + '/' + weiboutil.mid2url(status.mid);
69 }
70
71 // geo: { type: 'Point', coordinates: [ 22.354231, 113.421234 ] } latitude, longitude
72266 if (status.geo && status.geo.type === 'Point' && status.geo.coordinates) {
7322 var geo = {
74 latitude: String(status.geo.coordinates[0]),
75 longitude: String(status.geo.coordinates[1]),
76 };
7722 status.geo = geo;
78 }
79
80266 if (status.retweeted_status) {
8152 status.retweeted_status = this.format_status(status.retweeted_status, args);
8252 if (!status.retweeted_status.t_url) {
833 status.retweeted_status.t_url =
84 'http://weibo.com/' + status.user.id + '/' + weiboutil.mid2url(status.retweeted_status.mid);
85 }
86 }
87266 return status;
88};
89
901WeiboAPI.prototype.format_user = function (user, args) {
91370 user.id = user.idstr;
92370 user.created_at = new Date(user.created_at);
93370 user.t_url = 'http://weibo.com/' + (user.domain || user.id);
94370 if (user.status) {
953 user.status = this.format_status(user.status, args);
963 if (!user.status.t_url) {
973 user.status.t_url = user.t_url + '/' + weiboutil.mid2url(user.status.mid || user.status.id);
98 }
99 }
100370 return user;
101};
102
1031WeiboAPI.prototype.format_comment = function (comment, args) {
104109 comment.id = comment.idstr;
105109 comment.created_at = new Date(comment.created_at);
106109 if (comment.user) {
107109 comment.user = this.format_user(comment.user, args);
108 }
109109 if (comment.status) {
11079 comment.status = this.format_status(comment.status, args);
111 }
112109 if (comment.reply_comment) {
11330 comment.reply_comment = this.format_comment(comment.reply_comment, args);
114 }
115109 return comment;
116};
117
1181WeiboAPI.prototype.format_message = function (message, args) {
1190 return message;
120};
121
1221WeiboAPI.prototype.format_emotion = function (emotion, args) {
1230 return emotion;
124};
125
1261WeiboAPI.prototype.format_count = function (count, args) {
1272 count.id = String(count.id);
1282 return count;
129};
130
1311WeiboAPI.prototype.format_favorite = function (favorite, args) {
13220 favorite.status = this.format_status(favorite.status);
13320 favorite.created_at = new Date(favorite.favorited_time);
13420 delete favorite.favorited_time;
13520 return favorite;
136};
137
138/**
139 * User
140 */
141
1421WeiboAPI.prototype.verify_credentials = function (user, callback) {
1431 var uid = user.uid || user.id;
1441 return this.user_show(user, uid, null, callback);
145};
146

tbase_oauth_v2.js

87%
55
48
7
LineHitsSource
1/*!
2 * node-weibo - lib/tbase_oauth_v2.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var TBase = require('./tbase');
141var inherits = require('util').inherits;
151var utils = require('./utils');
161var querystring = require('querystring');
17
18/**
19 * TAPI Base class, support OAuth v2.0
20 */
211function TBaseOauthV2() {
223 TBaseOauthV2.super_.call(this);
233 this.config.oauth_version = '2.0';
24}
25
261inherits(TBaseOauthV2, TBase);
271module.exports = TBaseOauthV2;
28
29/**
30 * Result formatters
31 */
32
331TBaseOauthV2.prototype.format_access_token = function (token) {
340 token = JSON.parse(token);
350 return token;
36};
37
38/**
39 * OAuth
40 */
41
421TBaseOauthV2.prototype.convert_token = function (user) {
436 var params = {
44 redirect_uri: user.oauth_callback || this.config.oauth_callback,
45 client_id: this.config.appkey,
46 response_type: 'code',
47 };
486 var oauth_scope = user.oauth_scope || this.config.oauth_scope;
496 if (oauth_scope) {
500 params.oauth_scope = oauth_scope;
51 }
526 if (user.state) {
53 // An unguessable random string. It is used to protect against cross-site request forgery attacks.
540 params.state = user.state;
55 }
566 return params;
57};
58
591TBaseOauthV2.prototype.get_authorization_url = function (user, callback) {
604 var data = this.convert_token(user);
614 data.response_type = 'code';
624 var info = {
63 blogtype: user.blogtype,
64 auth_url: this.format_authorization_url(data)
65 };
664 process.nextTick(function () {
674 callback(null, info);
68 });
694 return this;
70};
71
721TBaseOauthV2.prototype.get_access_token = function (user, callback) {
732 var params = {
74 type: 'POST',
75 user: user,
76 playload: 'string',
77 api_host: this.config.oauth_host,
78 request_method: 'get_access_token'
79 };
802 var data = this.convert_token(user);
812 data.grant_type = 'authorization_code';
822 data.client_secret = this.config.secret;
832 var code = user.code || user.oauth_verifier || user.oauth_pin;
842 if (code) {
850 data.code = code;
86 }
87
882 params.data = data;
892 var self = this;
902 var url = self.config.oauth_access_token;
912 self.send_request(url, params, function (err, token) {
922 if (err) {
931 return callback(err);
94 }
95 // { access_token: '2.00EkofzBtMpzNBb9bc3108d8MwDTTE',
96 // remind_in: '633971',
97 // expires_in: 633971,
98 // uid: '1827455832' }
991 token = self.format_access_token(token);
1001 if (!token.access_token) {
1011 var message = token.error || JSON.stringify(token);
1021 err = new Error(message);
1031 err.data = token;
1041 err.name = self.errorname('get_access_token');
1051 return callback(err);
106 }
1070 token.blogtype = user.blogtype;
1080 callback(null, token);
109 });
1102 return this;
111};
112
1131TBaseOauthV2.prototype.apply_auth = function (url, args, user) {
11449 args.data = args.data || {};
11549 args.data.access_token = user.access_token;
116};
117

github.js

95%
24
23
1
LineHitsSource
1/*!
2 * node-weibo - lib/github.js
3 * Copyright(c) 2012 fengmk2 <fengmk2@gmail.com>
4 * MIT Licensed
5 */
6
71"use strict";
8
9/**
10 * Module dependencies.
11 */
12
131var TBaseOauthV2 = require('./tbase_oauth_v2');
141var inherits = require('util').inherits;
151var TSinaAPI = require('./tsina');
161var utils = require('./utils');
171var querystring = require('querystring');
18
19
201function GithubAPI(options) {
211 GithubAPI.super_.call(this);
221 var config = utils.extend({}, options, {
23 host: 'https://api.github.com',
24 result_format: '',
25 oauth_key: '',
26 oauth_secret: '',
27 oauth_host: 'https://github.com',
28 oauth_authorize: '/login/oauth/authorize',
29 oauth_access_token: '/login/oauth/access_token',
30
31 verify_credentials: '/user',
32 user_show: '/users/{{uid}}',
33
34 support_favorites: false,
35 });
361 this.init(config);
37}
38
391inherits(GithubAPI, TBaseOauthV2);
401module.exports = GithubAPI;
41
42/**
43 * Utils methods
44 */
45
461GithubAPI.prototype.url_encode = function (text) {
470 return text;
48};
49
50/**
51 * OAuth
52 */
53
541GithubAPI.prototype.convert_token = function (user) {
553 var data = GithubAPI.super_.prototype.convert_token.call(this, user);
563 data.state = Date.now();
573 return data;
58};
59
60/**
61 * Result formatters
62 */
63
641GithubAPI.prototype.format_access_token = function (token) {
651 token = querystring.parse(token);
661 return token;
67};
68
69/**
70 *
71{
72 public_repos: 67,
73 following: 84,
74 created_at: '2009-11-21T08:07:35Z',
75 type: 'User',
76 email: 'fengmk2@gmail.com',
77 bio: 'nodejs',
78 blog: 'http://fengmk2.github.com',
79 location: 'Hangzhou, China',
80 gravatar_id: '95b9d41231617a05ced5604d242c9670',
81 avatar_url: 'https://secure.gravatar.com/avatar/95b9d41231617a05ced5604d242c9670?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png',
82 public_gists: 21,
83 followers: 293,
84 login: 'fengmk2',
85 name: 'fengmk2',
86 company: 'http://www.taobao.com/',
87 id: 156269,
88 html_url: 'https://github.com/fengmk2',
89 hireable: false,
90 url: 'https://api.github.com/users/fengmk2'
91}
92 */
931GithubAPI.prototype.format_user = function (data) {
942 var user = {
95 id: data.login,
96 t_url: data.html_url,
97 screen_name: data.name,
98 name: data.login,
99 location: data.location,
100 url: data.blog || data.url,
101 profile_image_url: data.avatar_url + '&s=50',
102 avatar_large: data.avatar_url + '&s=180',
103 gender: 'n',
104 following: false,
105 verified: false,
106 follow_me: false,
107 followers_count: data.followers,
108 friends_count: data.following,
109 statuses_count: data.public_repos,
110 favourites_count: 0,
111 created_at: new Date(data.created_at),
112 email: data.email,
113 };
1142 return user;
115};
116
117

base64.js

41%
90
37
53
LineHitsSource
1/**
2*
3* Base64 encode / decode
4* http://www.webtoolkit.info/
5*
6**/
7
8// support atob and btoa native method in browser
9
101(function () {
11
121var Base64 = {
13
14 // private property
15 _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
16
17 // public method for encoding
18 encode: function (input) {
198 var output = "";
208 var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
218 var i = 0;
22
238 input = Base64._utf8_encode(input);
24
258 while (i < input.length) {
26
27158 chr1 = input.charCodeAt(i++);
28158 chr2 = input.charCodeAt(i++);
29158 chr3 = input.charCodeAt(i++);
30
31158 enc1 = chr1 >> 2;
32158 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
33158 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
34158 enc4 = chr3 & 63;
35
36158 if (isNaN(chr2)) {
370 enc3 = enc4 = 64;
38158 } else if (isNaN(chr3)) {
394 enc4 = 64;
40 }
41
42158 output = output +
43 this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
44 this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
45
46 }
47
488 return output;
49 },
50
51 // public method for decoding
52 decode: function (input) {
530 var output = "";
540 var chr1, chr2, chr3;
550 var enc1, enc2, enc3, enc4;
560 var i = 0;
57
580 input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
59
600 while (i < input.length) {
61
620 enc1 = this._keyStr.indexOf(input.charAt(i++));
630 enc2 = this._keyStr.indexOf(input.charAt(i++));
640 enc3 = this._keyStr.indexOf(input.charAt(i++));
650 enc4 = this._keyStr.indexOf(input.charAt(i++));
66
670 chr1 = (enc1 << 2) | (enc2 >> 4);
680 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
690 chr3 = ((enc3 & 3) << 6) | enc4;
70
710 output = output + String.fromCharCode(chr1);
72
730 if (enc3 != 64) {
740 output = output + String.fromCharCode(chr2);
75 }
760 if (enc4 != 64) {
770 output = output + String.fromCharCode(chr3);
78 }
79
80 }
81
820 output = Base64._utf8_decode(output);
83
840 return output;
85
86 },
87
88 // private method for UTF-8 encoding
89 _utf8_encode : function (string) {
908 string = string.replace(/\r\n/g,"\n");
918 var utftext = "";
92
938 for (var n = 0; n < string.length; n++) {
94
95228 var c = string.charCodeAt(n);
96
97228 if (c < 128) {
98102 utftext += String.fromCharCode(c);
99 }
100126 else if ((c > 127) && (c < 2048)) {
10110 utftext += String.fromCharCode((c >> 6) | 192);
10210 utftext += String.fromCharCode((c & 63) | 128);
103 }
104 else {
105116 utftext += String.fromCharCode((c >> 12) | 224);
106116 utftext += String.fromCharCode(((c >> 6) & 63) | 128);
107116 utftext += String.fromCharCode((c & 63) | 128);
108 }
109
110 }
111
1128 return utftext;
113 },
114
115 // private method for UTF-8 decoding
116 _utf8_decode : function (utftext) {
1170 var string = "";
1180 var i = 0;
1190 var c = 0, c1 = 0, c2 = 0;
120
1210 while ( i < utftext.length ) {
122
1230 c = utftext.charCodeAt(i);
124
1250 if (c < 128) {
1260 string += String.fromCharCode(c);
1270 i++;
128 }
1290 else if((c > 191) && (c < 224)) {
1300 c2 = utftext.charCodeAt(i+1);
1310 string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
1320 i += 2;
133 }
134 else {
1350 c2 = utftext.charCodeAt(i+1);
1360 c3 = utftext.charCodeAt(i+2);
1370 string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
1380 i += 3;
139 }
140 }
1410 return string;
142 },
143 /**
144 * A str encode and decode use on phpwind
145 *
146 * e.g.: var my_key = 'awejfosjdlxldfjlsdfwerwljxoasldf!@##@'
147 * // encode
148 * var encode_str = Base64.strcode('fawave发威', my_key);
149 * // decode
150 * var source_str = Base64.strcode(encode_str, my_key, true)
151 *
152 * @param {String} str
153 * @param {String} key
154 * @param {Boolen} decode, default is `false`
155 * @return {String} encode or decode string
156 * @api public
157 */
158 strcode: function (str, key, decode) {
1590 var keybuffer = this.utf8_encode(key);
1600 var key_length = keybuffer.length;
1610 var buffer = null, encoding = 'base64';
1620 if(decode) {
1630 buffer = this.decode(str);
164 } else {
1650 buffer = this.utf8_encode(str);
166 }
1670 var buf = '';
1680 for (var i = 0, len = buffer.length; i < len; i++) {
1690 var k = i % key_length;
1700 buf += String.fromCharCode(buffer.charCodeAt(i) ^ keybuffer.charCodeAt(k));
171 }
1720 if (decode) {
1730 return this.utf8_decode(buffer);
174 } else {
1750 return this.encode(buffer);
176 }
177 }
178};
179
1801Base64.utf8_encode = Base64._utf8_encode;
1811Base64.utf8_decode = Base64._utf8_decode;
182
1831var root = this; // window on browser
1841if (typeof module === 'undefined') {
1850 root.weibo = root.weibo || {};
1860 root.weibo.base64 = Base64;
187} else {
1881 module.exports = Base64;
189}
190
191})();