Getting ready to publish
This commit is contained in:
parent
0e8a2bbd2c
commit
d638d813f7
27
.vscode/launch.json
vendored
Normal file
27
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"program": "${workspaceFolder}\\src\\main.js"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Test Program",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"program": "${workspaceFolder}\\src\\TenorJSv2.test.js"
|
||||
}
|
||||
]
|
||||
}
|
30
.vscode/snips.code-snippets
vendored
Normal file
30
.vscode/snips.code-snippets
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
// Place your custom-scripts workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Placeholders with the same ids are connected.
|
||||
// Example:
|
||||
// "Print to console": {
|
||||
// "scope": "javascript,typescript",
|
||||
// "prefix": "log",
|
||||
// "body": [
|
||||
// "console.log('$1');",
|
||||
// "$2"
|
||||
// ],
|
||||
// "description": "Log output to console"
|
||||
// }
|
||||
|
||||
"Banner Comment": {
|
||||
"scope": "javascript,typescript",
|
||||
"description": "Banner Comment",
|
||||
"prefix": "///",
|
||||
"body": [
|
||||
"// #############################################################",
|
||||
"// $1",
|
||||
"// #############################################################",
|
||||
"$2"
|
||||
]
|
||||
}
|
||||
}
|
128
package-lock.json
generated
Normal file
128
package-lock.json
generated
Normal file
@ -0,0 +1,128 @@
|
||||
{
|
||||
"name": "tenorjs-v2",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "tenorjs-v2",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.7",
|
||||
"dotenv": "^16.4.5"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.4.5",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
|
||||
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
23
package.json
Normal file
23
package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "tenorjs-v2",
|
||||
"version": "1.0.1",
|
||||
"description": "A crappy wrapper for Tenor.com API",
|
||||
"main": "./src/index.js",
|
||||
"scripts": {
|
||||
"test": "node ./src/test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.vfsh.dev/voidf1sh/tenorjs-v2"
|
||||
},
|
||||
"keywords": [
|
||||
"tenor",
|
||||
"gif"
|
||||
],
|
||||
"author": "Skylar Grant",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.7",
|
||||
"dotenv": "^16.4.5"
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
module.exports = class Request {
|
||||
constructor() {
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
get(url, callback) {
|
||||
const request = new XMLHttpRequest();
|
||||
|
||||
// Set up the callback for when the response comes in
|
||||
request.onreadystatechange = () => {
|
||||
if (request.readyState === 4) {
|
||||
this.completed = true;
|
||||
if (request.status === 200) {
|
||||
callback(null, request.responseText);
|
||||
} else {
|
||||
callback(new Error(`Request failed with status ${request.status}: ${request.statusText}`), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the request, as asynchronous
|
||||
request.open("GET", url, true);
|
||||
|
||||
// Send the request
|
||||
request.send(null);
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
const Request = require('./Request');
|
||||
|
||||
module.exports = {
|
||||
Tenor: class Tenor {
|
||||
constructor(tenorApiKey) {
|
||||
this.token = tenorApiKey;
|
||||
}
|
||||
|
||||
async configure() {
|
||||
// Sanity tests
|
||||
if (!this.token) {
|
||||
throw new Error('No API key provided');
|
||||
}
|
||||
|
||||
// Search with an invalid key
|
||||
try {
|
||||
const response = await this.search('invalid', 'query', 1);
|
||||
if (response.code !== 16) {
|
||||
throw new Error('Unexpected response from known-bad API query.');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Error searching with invalid key: ${error.message}`);
|
||||
}
|
||||
|
||||
// Search with a valid key
|
||||
try {
|
||||
const response = await this.search(this.token, 'cat', 1);
|
||||
if (response.code === 16) {
|
||||
throw new Error(response.error);
|
||||
} else if (response.results.length !== 1) {
|
||||
throw new Error('Unexpected response from known-good API query.');
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Error searching with valid key: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
search(token, query, limit) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Form the search url
|
||||
const searchUrl = `https://g.tenor.com/v1/search?q=${query}&key=${token}&limit=${limit}`;
|
||||
|
||||
// Submit the search
|
||||
new Request().get(searchUrl, (error, response) => {
|
||||
if (error) {
|
||||
return reject(new Error(`Error fetching GIFs: ${error.message}`));
|
||||
}
|
||||
try {
|
||||
// Parse the json response
|
||||
const responseJson = JSON.parse(response);
|
||||
resolve(responseJson);
|
||||
} catch (parseError) {
|
||||
reject(new Error(`Error parsing response: ${parseError.message}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
109
src/index.js
Normal file
109
src/index.js
Normal file
@ -0,0 +1,109 @@
|
||||
// #############################################################
|
||||
// Module Imports
|
||||
// #############################################################
|
||||
const axios = require('axios');
|
||||
|
||||
// #############################################################
|
||||
// Variables
|
||||
// #############################################################
|
||||
const baseUrl = "https://tenor.googleapis.com/v2/search?";
|
||||
|
||||
class Request {
|
||||
constructor() {
|
||||
this.completed = false;
|
||||
}
|
||||
|
||||
get(url) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
axios.get(url).then((response) => {
|
||||
this.completed = true;
|
||||
resolve(response.data);
|
||||
}).catch((error) => {
|
||||
this.completed = true;
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Tenor {
|
||||
constructor(tenorApiKey) {
|
||||
this.token = tenorApiKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
async configure() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.token) {
|
||||
return reject(new Error('No API key provided'));
|
||||
}
|
||||
|
||||
this.search('cat', 1).then((gifs) => { // Search with a valid key
|
||||
if (gifs.length !== 1) { // Check for a valid response
|
||||
return reject(new Error('Unexpected response from known-good API query.'));
|
||||
} else { // Key is valid
|
||||
resolve('Tenor API key is valid and API is working correctly.');
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.response && error.response.status === 401) {
|
||||
return reject(new Error(`Invalid API Key`));
|
||||
} else {
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
search(query, options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Form the search url
|
||||
let searchUrl = baseUrl + `q=${query}`;
|
||||
searchUrl += `&key=${this.token}`;
|
||||
if (options) {
|
||||
if (typeof options === 'number') {
|
||||
searchUrl += `&limit=${options}`;
|
||||
} else if (typeof options === 'object') {
|
||||
if (options.limit) searchUrl += `&limit=${options.limit}`; // Default 20 max 50
|
||||
if (options.clientKey) searchUrl += `&client_key=${options.clientKey}`;
|
||||
if (options.random) searchUrl += `&random=${options.random}`;
|
||||
if (options.position) searchUrl += `&pos=${options.position}`;
|
||||
if (options.country) searchUrl += `&country=${options.country}`;
|
||||
if (options.locale) searchUrl += `&locale=${options.locale}`;
|
||||
if (options.mediaFilter) searchUrl += `&media_filter=${options.mediaFilter}`;
|
||||
if (options.contentFilter) searchUrl += `&contentfilter=${options.contentFilter}`;
|
||||
}
|
||||
}
|
||||
// Submit the search
|
||||
new Request().get(searchUrl).then((response) => {
|
||||
// Validate the results
|
||||
if (!response.results || response.results.length === 0) {
|
||||
return reject(new Error('No results found'));
|
||||
}
|
||||
|
||||
// Create object to store results
|
||||
const gifs = [];
|
||||
// Manipulate the results
|
||||
for (const gif of response.results) {
|
||||
gifs.push(new Gif(gif));
|
||||
}
|
||||
|
||||
// Return the results
|
||||
resolve(gifs);
|
||||
}).catch(error => {
|
||||
return reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Gif {
|
||||
constructor(gif) {
|
||||
this.url = gif.url;
|
||||
this.id = gif.id;
|
||||
this.created = gif.created;
|
||||
this.mediaUrl = gif.media_formats.gif.url;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Tenor;
|
45
src/main.js
45
src/main.js
@ -1,45 +0,0 @@
|
||||
// callback for the top 8 GIFs of search
|
||||
function tenorCallback_search(responsetext)
|
||||
{
|
||||
// parse the json response
|
||||
var response_objects = JSON.parse(responsetext);
|
||||
|
||||
top_10_gifs = response_objects["results"];
|
||||
|
||||
// load the GIFs -- for our example we will load the first GIFs preview size (nanogif) and share size (tinygif)
|
||||
|
||||
document.getElementById("preview_gif").src = top_10_gifs[0]["media"][0]["nanogif"]["url"];
|
||||
|
||||
document.getElementById("share_gif").src = top_10_gifs[0]["media"][0]["tinygif"]["url"];
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// function to call the trending and category endpoints
|
||||
function grab_data()
|
||||
{
|
||||
// set the apikey and limit
|
||||
var apikey = "LIVDSRZULELA";
|
||||
var lmt = 8;
|
||||
|
||||
// test search term
|
||||
var search_term = "excited";
|
||||
|
||||
// using default locale of en_US
|
||||
var search_url = "https://g.tenor.com/v1/search?q=" + search_term + "&key=" +
|
||||
apikey + "&limit=" + lmt;
|
||||
|
||||
httpGetAsync(search_url,tenorCallback_search);
|
||||
|
||||
// data will be loaded by each call's callback
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// SUPPORT FUNCTIONS ABOVE
|
||||
// MAIN BELOW
|
||||
|
||||
// start the flow
|
||||
grab_data();
|
70
src/test.js
Normal file
70
src/test.js
Normal file
@ -0,0 +1,70 @@
|
||||
const Tenor = require('.');
|
||||
const dotenv = require('dotenv').config();
|
||||
|
||||
if (process.env.TENOR_API_KEY === undefined) {
|
||||
console.error('Please set TENOR_API_KEY in .env');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const tests = [
|
||||
// Valid API Key
|
||||
new Promise((resolve, reject) => {
|
||||
const tenor = new Tenor(process.env.TENOR_API_KEY);
|
||||
tenor.configure()
|
||||
.then((result) => {
|
||||
console.log('PASS: Valid API Key --', result);
|
||||
resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('FAIL: Valid API Key --', error.message);
|
||||
reject();
|
||||
});
|
||||
}),
|
||||
// Invalid API Key
|
||||
new Promise((resolve, reject) => {
|
||||
const tenor = new Tenor('invalidApiKey');
|
||||
tenor.configure()
|
||||
.then((result) => {
|
||||
console.log('FAIL: Invalid API Key');
|
||||
reject();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('PASS: Invalid API Key --', error.response.data.error.message);
|
||||
resolve();
|
||||
});
|
||||
}),
|
||||
// Valid query and limit
|
||||
new Promise((resolve, reject) => {
|
||||
const tenor = new Tenor(process.env.TENOR_API_KEY);
|
||||
// Form the options for the search
|
||||
const options = {
|
||||
limit: 5
|
||||
};
|
||||
|
||||
if (process.env.TENOR_CLIENT_KEY) {
|
||||
options.clientKey = process.env.TENOR_CLIENT_KEY;
|
||||
}
|
||||
tenor.search('dog', options)
|
||||
.then((gifs) => {
|
||||
if (Array.isArray(gifs) && gifs.length === 5) {
|
||||
console.log('PASS: valid query and limit --', gifs); // Expected output: Array of five Gif objects
|
||||
resolve();
|
||||
} else {
|
||||
console.error('FAIL: valid query and limit --', 'Unexpected response format'); // Show only the FAIL message
|
||||
reject();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('FAIL: valid query and limit --', error.message); // Show only the FAIL message
|
||||
reject();
|
||||
});
|
||||
})
|
||||
];
|
||||
|
||||
Promise.all(tests)
|
||||
.then(() => {
|
||||
console.log('All tests passed!');
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('Some tests failed!');
|
||||
});
|
Loading…
Reference in New Issue
Block a user