Skip to content

Instantly share code, notes, and snippets.

@inaniwa3
Created May 16, 2020 07:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save inaniwa3/f2ae59bf1e2e606ea07dc732b54f5196 to your computer and use it in GitHub Desktop.
Save inaniwa3/f2ae59bf1e2e606ea07dc732b54f5196 to your computer and use it in GitHub Desktop.
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const fs = require('fs');
const turf = require('@turf/turf');
const request = require('request');
const twitter = require('twitter-lite');
const config = require('./config');
const IMG_PATH = 'tmp/img.jpg';
const MAP_URL_PARAMS = '&heading=0&fov=90&pitch=90';
const MAX_ASK_COUNT = 20;
// https://stackoverflow.com/questions/41958271/load-google-maps-api-in-jsdom
const googleApi = () => new Promise(res => {
const { window } = new JSDOM(
`<!doctype html><html><head><script src="https://maps.googleapis.com/maps/api/js?key=${config.google.apiKey}&callback=ready"></script></head></html>`,
{ runScripts: 'dangerously', resources: 'usable' }
);
window.ready = () => {
res(window);
};
});
// https://shimz.me/blog/google-map-api/5116
// https://github.com/dataofjapan/land
function latLngCandidate() {
const geojson = JSON.parse(fs.readFileSync('./japan.geojson'));
while (true) {
const point = turf.randomPoint(1, {
bbox:[122.93349314581921, 20.422740332935973,
153.98686576033427, 45.557331085205114]
});
for (let i = 0; i < geojson.features.length; i++) {
if (turf.inside(point.features[0], geojson.features[i])) {
return [point.features[0].geometry.coordinates[1],
point.features[0].geometry.coordinates[0]];
}
}
}
}
// https://developers.google.com/maps/documentation/javascript/reference/street-view-service
function askStreetViewService(google, latLng) {
return new Promise(function(resolve, reject) {
const svs = new google.maps.StreetViewService();
svs.getPanorama({
location: new google.maps.LatLng(latLng[0], latLng[1]),
preference: google.maps.StreetViewPreference.BEST,
radius: 1000,
source: google.maps.StreetViewSource.OUTDOOR,
},
function(data, status) {
if (status === google.maps.StreetViewStatus.OK) {
resolve([data.location.latLng.lat(), data.location.latLng.lng()]);
} else {
resolve(false);
}
});
});
}
// https://developers.google.com/maps/documentation/streetview/intro
function downloadImage(latLng)
{
return new Promise(function(resolve, reject) {
const url = `https://maps.googleapis.com/maps/api/streetview?size=640x640&location=${latLng[0]},${latLng[1]}${MAP_URL_PARAMS}&source=outdoor&key=${config.google.apiKey}`;
request(
{ method: 'GET', url: url, encoding: null },
function(error, response, body) {
if (!error && response.statusCode === 200) {
fs.writeFileSync(IMG_PATH, body, 'binary');
resolve(true);
} else {
resolve(false);
}
}
);
});
}
// https://developers.google.com/maps/documentation/urls/guide#street-view-action
function createMapUrl(latLng)
{
return `https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${latLng[0]},${latLng[1]}${MAP_URL_PARAMS}`;
}
// https://dev.bitly.com/v4/#operation/createBitlink
function shortenUrl(longUrl) {
return new Promise(function(resolve, reject) {
request(
{
method: 'POST',
url: 'https://api-ssl.bitly.com/v4/shorten',
headers: {
'Authorization': `Bearer ${config.bitly.accessToken}`
},
json: {
'long_url': longUrl
}
},
function(error, response, body) {
if (!error && (response.statusCode === 200 || response.statusCode === 201)) {
resolve(body.link);
} else {
resolve(false);
}
}
);
});
}
// https://github.com/draftbit/twitter-lite
// https://developer.twitter.com/en/docs/api-reference-index
function tweet(text)
{
const clientUpload = new twitter({
subdomain: 'upload',
consumer_key: config.twitter.consumerKey,
consumer_secret: config.twitter.consumerSecret,
access_token_key: config.twitter.accessTokenKey,
access_token_secret: config.twitter.accessTokenSecret
});
const clientApi = new twitter({
subdomain: 'api',
consumer_key: config.twitter.consumerKey,
consumer_secret: config.twitter.consumerSecret,
access_token_key: config.twitter.accessTokenKey,
access_token_secret: config.twitter.accessTokenSecret
});
const media_data = fs.readFileSync(IMG_PATH).toString('base64');
(async () => {
const response = await clientUpload.post('media/upload', {
media_data: media_data,
media_category: 'tweet_image'
});
await clientApi.post('statuses/update', {
status: text,
media_ids: response.media_id_string
});
console.log('Probably tweeted.');
})();
}
async function main(doTweet=false) {
const window = await googleApi();
let latLng = false;
for (let i = 0; i < MAX_ASK_COUNT; i++) {
latLng = await askStreetViewService(window.google, latLngCandidate());
if (latLng) {
break;
}
}
if (!latLng) {
console.log('askStreetViewService() failed.');
window.close();
return;
}
const result = await downloadImage(latLng);
if (!result) {
console.log('downloadImage() failed.');
window.close();
return;
}
const mapUrl = createMapUrl(latLng);
console.log(mapUrl);
const shortUrl = await shortenUrl(mapUrl); // because of '@'
if (!shortUrl) {
console.log('shortenUrl() failed.');
window.close();
return;
}
console.log(shortUrl);
if (doTweet) {
tweet(shortUrl);
}
window.close();
return;
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment