Skip to content

Commit

Permalink
✨ v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
shawjia committed Sep 3, 2018
1 parent 5d556f3 commit a8ef4cb
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
extends: "airbnb-base"
};
73 changes: 73 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless
57 changes: 56 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,57 @@
# geektime
API client for time.geekbang.org

> API client for time.geekbang.org
## INSTALL
```bash
npm install geektime
# or
yarn add geektime
```

## EXAMPLE
```js
const Geektime = require('geektime');
const client = new Geektime('phone', 'pass');

(async () => {

try {
const products = await client.products();
console.log(products);
} catch (error) {
console.error(error);
}

})();
```

## API

### products()

产品列表,返回 专栏/视频课/微课/其他

### intro(cid)

返回专栏信息

### articles(cid, size = 1000)

返回专栏文章列表

### article(id)

返回单篇文章详情

### audios(cid, size = 1000)

返回音频列表

### NOTE
- *cid:* 专栏 id
- *id:* 文章 id

## License

MIT © [shawjia](https://github.com/shawjia)
113 changes: 113 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const got = require('got');

const host = 'https://time.geekbang.org/serv/v1';
const links = {
login: 'https://account.geekbang.org/account/ticket/login',
products: `${host}/my/products/all`,
intro: `${host}/column/intro`,
articles: `${host}/column/articles`,
article: `${host}/article`,
audios: `${host}/column/audios`,
};

async function request(link, body = {}, cookie = '') {
const headers = {
Referer: 'https://servicewechat.com/wxc4f8a61ef62e6e35/20/page-frame.html',
Cookie: cookie,
};

try {
const isLoginLink = link === links.login;
const res = await got(link, {
json: true, headers, body, method: 'post',
});
const loginCookie = isLoginLink
? res.headers['set-cookie'].map(v => v.split(';')[0]).join('; ')
: '';

if (res.body.code === 0) {
return isLoginLink
? { ...res.body.data, cookie: loginCookie }
: res.body.data;
}

throw new Error(`Wrong Code: ${res.body.code}`);
} catch (err) {
throw err;
}
}

class Api {
constructor(cellphone, password) {
if (typeof cellphone !== 'string' || typeof password !== 'string') {
throw new TypeError('cellphone/password should be string');
}

if (cellphone === '' || password === '') {
throw new Error('cellphone/password should not be empty');
}

this.cellphone = cellphone;
this.password = password;
this.cookie = null;
}

// 产品列表,返回 专栏/视频课/微课/其他
async products() {
const cookie = await this.getCookie();

return request(links.products, null, cookie);
}

// 专栏介绍
async intro(cid) {
const cookie = await this.getCookie();

return request(links.intro, { cid }, cookie);
}

// 专栏文章列表
async articles(cid, size = 1000) {
const cookie = await this.getCookie();

return request(links.articles, { cid, size }, cookie);
}

// 单篇文章详情
async article(id) {
const cookie = await this.getCookie();

return request(links.article, { id }, cookie);
}

// 音频列表
async audios(cid, size = 1000) {
const cookie = await this.getCookie();

return request(links.audios, { cid, size }, cookie);
}

async getCookie() {
if (this.cookie) {
return this.cookie;
}

const { cellphone, password } = this;
const body = {
cellphone,
password,
country: 86,
remember: 1,
captcha: '',
platform: 4,
appid: 1,
};

const { cookie } = await request(links.login, body);
this.cookie = cookie;

return this.cookie;
}
}

module.exports = Api;
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "geektime",
"version": "1.0.0",
"description": "API client for time.geekbang.org",
"main": "index.js",
"repository": "https://github.com/shawjia/geektime",
"author": "shawjia",
"license": "MIT",
"scripts": {
"test": "ava"
},
"keywords": [
"geektime",
"API",
"极客时间"
],
"devDependencies": {
"ava": "^0.25.0",
"eslint": "^5.5.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.14.0"
},
"dependencies": {
"got": "^9.2.0"
}
}
42 changes: 42 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const test = require('ava');
const Api = require('.');

const { GEEKTIME_PHONE, GEEKTIME_PASS } = process.env;
const client = new Api(GEEKTIME_PHONE, GEEKTIME_PASS);

const cid = 48; // 左耳听风
const articleId = 14271; // "article_title": "高效学习:端正学习态度",

test('Api()', (t) => {
t.throws(() => new Api(), TypeError);
});

test('api.products()', async (t) => {
const res = await client.products();

t.true(res.length > 0);
});

test('api.intro(48)', async (t) => {
const res = await client.intro(cid);

t.is(res.column_title, '左耳听风');
});

test('api.articles(48)', async (t) => {
const res = await client.articles(cid);

t.true(res.list.length > 0);
});

test('api.article(14271)', async (t) => {
const res = await client.article(articleId);

t.true(res.article_title.startsWith('高效学习'));
});

test('api.audios(48)', async (t) => {
const res = await client.audios(cid);

t.true(res.list.length > 0);
});

0 comments on commit a8ef4cb

Please sign in to comment.