service/replies.js

/**
 *  Mail   : indexxuan@gmail.com
 *  Date   : Mon 13 Mar 2017 05:35:26 PM CST
 */

/**
 *  @module RepliesService
 */

'use strict'

module.exports = app => {
  /**
   * @class RepliesService
   * @extends app.Service
   */
  return class RepliesService extends app.Service {
    /**
     * @constructor
     * @param {Object} ctx - 请求的上下文
     */
    constructor (ctx) {
      super(ctx)
      this.root = `${app.config.root}/replies`
      // 默认有权限,后面会判断
      this.auth = true
      // 请求需要的一次性签名
      this.once = ''
    }

    /**
     * request
     * 封装统一的请求方法
     * @member
     * @param {String} query - 请求参数
     * @param {Object} opts - 请求选项
     * @returns {Promise} - @async
     */
    async request (query, opts) {
      const url = `${this.root}/${query}.json`
      opts = Object.assign({
        timeout: [ '30s', '30s' ],
        dataType: 'json'
      }, opts)

      return await this.ctx.curl(url, opts)
    }

    /**
     * show
     * 获取一个topic的全部回复
     * @method
     * @param {Object} params - 参数
     * @returns {Promise} - @async
     */
    async show (params) {
      const result = await this.request('show', {
        data: params
      })
      return result.data
    }

    /**
     * getOnce
     * 获取一次性签名的工具方法
     * @member
     * @param {String} content - 需要解析的内容
     */
    getOnce(content) {
      const onceRe = /value=\"(\d+)\" name="once"/
      const onces = onceRe.exec(content)
      /* istanbul ignore next */
      if (onces && onces[1]) {
        this.once = onces[1]
      }
    }

    /**
     * create
     * 创建一个回复
     * @method
     * @param {Object} params - 参数
     * @returns {Promise}
     */
    async create (params) {
      // @step1 获取准备数据
      const session = this.ctx.sessionid
      const token = this.ctx.token
      const headers = Object.assign({}, this.ctx.commonHeaders, { Cookie: `${session}; ${token}` })

      /* istanbul ignore else */
      if (session.includes('undefined')) {
        this.auth = false 
      }

      // @step2 进入创建页,获取once
      const url = `https://www.v2ex.com/t/${params.topic_id}`
      const r = await this.ctx.curl(url, {
        method: 'get',
        timeout: [ '30s', '30s' ],
        headers: headers,
        dataType: 'text'
      })


      // @step3 获取once
      this.getOnce(r.data)

      // @step4 设置请求参数
      const data = Object.assign(params, { once: this.once }, { content: params.content })

      // @step5 发起请求
      const result = await this.ctx.curl(url, {
        method: 'post',
        dataType: 'text',
        headers: headers,
        data: data
      })

      // @step6 设置API返回值
      let success = false 
      let topicUrl = ''
      /* istanbul ignore next */
      if (result && result.headers && result.headers.location) {
        topicUrl = `https://www.v2ex.com${result.headers.location}`
      }
      let msg = !this.auth ? '请先登录再回帖' : !!topicUrl ? 'ok' : '回帖未知错误'
      // parser error msg
      let problems = result.data.match(/class="problem"\>.*\<\/div>/)
      let problem = problems && problems[0]
      // 好牛逼的HTML标签正则, https://segmentfault.com/q/1010000008733200?_ea=1734789
      const tagRe = /<("[^"]*"|'[^']*'|[^'">])*>/g 
      const errorMsg = problem && problem.replace(tagRe, '').replace(/class=".*">/, '')
      /* istanbul ignore next */
      if (errorMsg) {
        msg = errorMsg || `${result.status},可能是过于频繁操作`
        success = false
      } else {
        success = true
      }
      return {
        result: this.auth && success,
        msg: msg,
        url: topicUrl,
        detail: app.config.env === 'prod' ? '' : result
      } 
    }
  } // /.class=>RepliesService
} // /.exports