This repository has been archived by the owner on Aug 11, 2021. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
407 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,115 @@ | ||
# repl.it-button | ||
# Repl.it API | ||
|
||
*A Node.js client for creating projects and executing code on [Repl.it](https://repl.it/).* | ||
|
||
## Installation | ||
|
||
With Yarn: | ||
|
||
``` | ||
$ yarn add repl.it-api | ||
``` | ||
|
||
With NPM: | ||
|
||
``` | ||
$ npm install repl.it-api | ||
``` | ||
|
||
## Documentation | ||
|
||
All of the asyncronous code in the documentation below will be expressed async/await syntax, it works equally well with Promises. | ||
|
||
### Instantiate a Client | ||
|
||
Before doing anything else you have to import `repl.it-api` and create a Repl.it client. | ||
|
||
```javascript | ||
const ReplitClient = require('repl.it-api') | ||
const client = new ReplitClient() | ||
``` | ||
|
||
`ReplitClient`'s constructor takes one argument: a number (in milliseconds) which is the timeout for code execution. The default is `3000`. | ||
|
||
### Create a Project & Connect | ||
|
||
You have to create a project, and connect to Repl.it's websocket to execute code and write files in. | ||
|
||
```javascript | ||
await client.create() | ||
await client.connect() | ||
``` | ||
|
||
`client#create` takes one argument: a string that should be a valid language. The default is `nodejs`. | ||
|
||
### Write to a File | ||
|
||
```javascript | ||
await client.write('file.js', 'console.log("Hello, world!")') | ||
``` | ||
|
||
You can also write to the main file, which is the file that is executed by Repl.it. | ||
|
||
```javascript | ||
await client.writeMain('console.log("Hello from the main file")') | ||
``` | ||
|
||
`client#write` takes two arguments: | ||
|
||
1. A string that should be the file name or path to the file. Please don't include a slash or `./` at the beginning. | ||
2. A string for the actial file content. | ||
|
||
`client#writeMain` only takes one argument, which is the same as the second argument to `client#write`. | ||
|
||
### Run the Project | ||
|
||
Now you probably want to actually run your project! | ||
|
||
```javascript | ||
const result = await client.run({ | ||
output: (output) => console.log('Output:', output.trim()), | ||
timedOut: () => console.log('Timed out!'), | ||
installStart: () => console.log('Install start'), | ||
installOutput: (output) => console.log('Install output:', output.trim()), | ||
installEnd: () => console.log('Install end'), | ||
listen: (port) => console.log('Listening on port', port) | ||
}) | ||
``` | ||
|
||
`client#run` takes one argument that should be an object with a bunch event listeners, all documented below. They are all optional. | ||
|
||
- `output`: fired when the program outputs text, has one argument: a string containing the output. You may want to trim the whitespace. | ||
- `timedOut`: fired when the program execution times out. This timeout period **does not include** package installation time. | ||
- `installStart`: fired when package installation begins. This will only happen when there are packages that need to be installed. | ||
- `installOutput`: fired when package installation outputs text, has one argument: a string containing the output. You may want to trim the whitespace. | ||
- `installEnd`: fired when package installation finishes. | ||
- `installOutput`: fired when program listens on a port, has one argument: a number containing the port. You may want to trim the whitespace. | ||
|
||
`client#run` will also resolve with the output of the program which will most likely be `'undefined'`. Also, note that when the project either times out or listens on a port it resolves immediately. | ||
|
||
### Close the Connection | ||
|
||
We **super ultra very much recommend** doing this before exiting your program. It's as simple as the below code. | ||
|
||
```javascript | ||
await client.close() | ||
``` | ||
|
||
### Get Project Info | ||
|
||
```javascript | ||
const info = await client.getInfo() | ||
``` | ||
|
||
`info` will be an object in the following format: | ||
|
||
```javascript | ||
{ | ||
id: '7ab11c71-5efd-4b31-9136-686573e2455d', | ||
url: 'https://repl.it/repls/FastFlakyProblems', | ||
slug: 'FastFlakyProblems', | ||
language: 'nodejs' | ||
} | ||
``` | ||
|
||
Of course, your values will be different. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
const ReplitClient = require('./lib') | ||
const client = new ReplitClient() | ||
|
||
client.create().then(() => { | ||
console.log('Created') | ||
return client.connect() | ||
}).then(() => { | ||
console.log('Connected') | ||
return client.writeMain('require(\'express\')().listen()') | ||
}).then(() => { | ||
console.log('Wrote') | ||
return client.run({ | ||
output: (output) => console.log('Output:', output.trim()), | ||
timedOut: () => console.log('Timed out!'), | ||
installStart: () => console.log('Install start'), | ||
installOutput: (output) => console.log('Install output:', output.trim()), | ||
installEnd: () => console.log('Install end'), | ||
listen: (port) => console.log('Listening on port', port) | ||
}) | ||
}).then((result) => { | ||
console.log('Result:', result) | ||
return client.close() | ||
}).then(() => { | ||
console.log(client.getInfo()) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
const { CookieJar } = require('tough-cookie') | ||
const nodeFetch = require('node-fetch') | ||
const fetchCookie = require('fetch-cookie') | ||
const WebSocketClient = require('websocket').client | ||
|
||
const parseJson = (response) => response.json() | ||
const sendJson = (connection, json) => connection.sendUTF(JSON.stringify(json)) | ||
const headers = { | ||
'Referer': 'https://repl.it/languages/nodejs', | ||
'Content-Type': 'application/json' | ||
} | ||
|
||
module.exports = class { | ||
constructor(timeout = 5000) { | ||
this.got = {} | ||
this.fetch = fetchCookie(nodeFetch, new CookieJar()) | ||
this.timeout = timeout | ||
} | ||
|
||
async create(language = 'nodejs') { | ||
const { id, url, fileNames, slug } = await this.fetch('https://repl.it/data/repls/new', { | ||
method: 'POST', | ||
body: JSON.stringify({ language }), | ||
headers | ||
}).then(parseJson) | ||
this.got.id = id | ||
this.got.url = url | ||
this.got.slug = slug | ||
this.got.language = language | ||
this.got.mainFile = fileNames[0] | ||
|
||
this.got.token = await this.fetch(`https://repl.it/data/repls/${id}/gen_repl_token`, { | ||
method: 'POST', | ||
body: JSON.stringify({ | ||
liveCodingToken: null, | ||
polygott: false | ||
}), | ||
headers | ||
}).then(parseJson) | ||
} | ||
|
||
async connect() { | ||
const connection = await new Promise((resolve, reject) => { | ||
const client = new WebSocketClient() | ||
|
||
client.on('connectFailed', (error) => { | ||
reject(error) | ||
}) | ||
|
||
client.on('connect', (connection) => { | ||
resolve(connection) | ||
}) | ||
|
||
client.connect('wss://eval.repl.it/ws') | ||
}) | ||
await new Promise((resolve) => { | ||
sendJson(connection, { | ||
command: 'auth', | ||
data: this.got.token | ||
}) | ||
connection.on('message', ({ type, utf8Data }) => { | ||
if (type !== 'utf8') return | ||
const { command } = JSON.parse(utf8Data) | ||
if (command === 'ready') resolve() | ||
}) | ||
}) | ||
this.got.connection = connection | ||
} | ||
|
||
async write(name, content) { | ||
const json = await this.fetch(`https://repl.it/data/repls/signed_urls/${this.got.id}/${encodeURIComponent(name)}?d=${Date.now()}`).then(parseJson) | ||
const writeUrl = json.urls_by_action.write | ||
await this.fetch(writeUrl, { | ||
method: 'PUT', | ||
body: content, | ||
headers: { | ||
'Content-Type': '' | ||
} | ||
}) | ||
} | ||
|
||
writeMain(content) { | ||
return this.write(this.got.mainFile, content) | ||
} | ||
|
||
run(listeners = {}) { | ||
const { output, timedOut, listen, installStart, installOutput, installDone } = listeners | ||
let alreadyLeft = false | ||
let timeout | ||
|
||
return new Promise((resolve) => { | ||
const timeoutAmount = this.timeout | ||
function setTheTimeout() { | ||
timeout = setTimeout(() => { | ||
if (alreadyLeft) return | ||
alreadyLeft = true | ||
timedOut && timedOut() | ||
resolve() | ||
}, timeoutAmount) | ||
} | ||
|
||
sendJson(this.got.connection, { | ||
command: 'runProject', | ||
data: '[]' | ||
}) | ||
setTheTimeout() | ||
this.got.connection.on('message', ({ type, utf8Data }) => { | ||
if (type !== 'utf8') return | ||
const { command, data } = JSON.parse(utf8Data) | ||
|
||
if (command === 'event:packageInstallStart') { | ||
clearTimeout(timeout) | ||
installStart && installStart() | ||
} else if (installOutput && command === 'event:packageInstallOutput') { | ||
installOutput(data) | ||
} else if (command === 'event:packageInstallEnd') { | ||
setTheTimeout() | ||
installDone && installDone() | ||
} else if (output && command === 'output') { | ||
output(data) | ||
} else if (command === 'result' && !alreadyLeft) { | ||
alreadyLeft = true | ||
resolve(data) | ||
} else if (command === 'event:portOpen') { | ||
const { port } = JSON.parse(data) | ||
alreadyLeft = true | ||
listen && listen(port) | ||
resolve() | ||
} | ||
}) | ||
}) | ||
} | ||
|
||
close() { | ||
return new Promise((resolve) => { | ||
this.got.connection.close() | ||
this.got.connection.on('close', () => { | ||
resolve() | ||
}) | ||
}) | ||
} | ||
|
||
getInfo() { | ||
return { | ||
id: this.got.id, | ||
url: `https://repl.it${this.got.url}`, | ||
slug: this.got.slug, | ||
language: this.got.language | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"name": "repl.it-api", | ||
"version": "0.0.1", | ||
"description": "A Node.js client for creating projects and executing code on Repl.it.", | ||
"main": "lib.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/arch-lord/repl.it-api.git" | ||
}, | ||
"author": "Felix Mattick <felix.mattick@gmail.com>", | ||
"license": "MIT", | ||
"private": false, | ||
"dependencies": { | ||
"fetch-cookie": "^0.7.2", | ||
"node-fetch": "^2.3.0", | ||
"tough-cookie": "^3.0.1", | ||
"websocket": "^1.0.28" | ||
} | ||
} |
Oops, something went wrong.