Skip to content

Commit

Permalink
wip: journeys part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
derhuerst committed Mar 8, 2018
1 parent 138ffec commit 0933f2f
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 21 deletions.
9 changes: 2 additions & 7 deletions commands/departures/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
'use strict'

const searchStations = require('vbb-stations-autocomplete')
const allStations = require('vbb-stations/simple')
const parseTime = require('parse-messy-time')
const linesAt = require('vbb-lines-at')
const hafas = require('vbb-hafas')

const findStation = require('../../lib/find-station')
const commandKeys = require('../../lib/commands-keyboard')
const whenKeys = require('../../lib/when-keyboard')
const getFrequentStationsKeys = require('../../lib/frequent-stations-keyboard')
Expand All @@ -25,11 +24,7 @@ I don't know about this station, please double-check for typos.
If you're sure it's my fault, please let my creator @derhuerst know.`

const parseWhere = async (where, ctx) => {
await ctx.replyWithChatAction('typing')
let [station] = searchStations(where, 1, false, false) // non-fuzzy
if (!station) [station] = searchStations(where, 1, true, false) // fuzzy
if (station) station = allStations.find(s => s.id === station.id) // get details

const station = findStation(where)
if (station) await ctx.replyWithMarkdown(`I found ${station.name}.`)
else await ctx.replyWithMarkdown(unknownStation)
return station
Expand Down
11 changes: 0 additions & 11 deletions commands/journeys.js

This file was deleted.

106 changes: 106 additions & 0 deletions commands/journeys/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use strict'

const parseTime = require('parse-messy-time')
const Markup = require('telegraf/markup')
const hafas = require('vbb-hafas')

const findStation = require('../../lib/find-station')
const commandKeys = require('../../lib/commands-keyboard')
const whenKeys = require('../../lib/when-keyboard')
const getFrequentStationsKeys = require('../../lib/frequent-stations-keyboard')
const renderJourney = require('./render')

const promptWhen = `\
*When?*
e.g. "now", "in 10 minutes" or "tomorrow 17:20"`
const unknownTime = `\
Hm, don't understand this format.`

const promptOrigin = `\
*Where do you start?*
Enter a location like "U mehringdamm", "Kaiserdamm 26" or send your location.`
const promptDest = `\
*Where do you want to go?*`
// const unknownStation = `\
// I don't know about this station, please double-check for typos.
// If you're sure it's my fault, please let my creator @derhuerst know.`

const parseWhere = async (where, ctx) => {
// todo
}

const parseWhen = async (when, ctx) => {
when = +parseTime(when, {now: new Date()})
if (Number.isNaN(when)) {
await ctx.replyWithMarkdown(unknownTime)
return null
}
return when
}

const getFrequentStationKeys = async (ctx) => {
const ids = await ctx.storage.getTopLocations()
return getFrequentStationsKeys(ids, [
// Markup.locationRequestButton('use current location') // todo
])
}

const journeys = async (ctx, next) => {
// `/a spichernstr` shorthand
if (ctx.args && ctx.args[0]) {
const station = await parseWhere(ctx.args[0], ctx)
if (station) await ctx.storage.putData('origin', station)
return next()
}
if (ctx.command) {
await ctx.replyWithMarkdown(promptOrigin, getFrequentStationKeys(ctx))
return next() // await next message
}

let origin = await ctx.storage.getData('origin')
if (!origin) {
origin = await parseWhere(ctx.message.text, ctx)
if (!origin) return next() // await next message
await ctx.storage.putData('origin', origin)
await ctx.storage.incLocation(origin.id)

await ctx.replyWithMarkdown(promptDest, getFrequentStationKeys(ctx))
return next() // await next message
}

let destination = await ctx.storage.getData('destination')
if (!destination) {
destination = await parseWhere(ctx.message.text, ctx)
if (!destination) return next() // await next message
await ctx.storage.putData('destination', destination)
await ctx.storage.incLocation(destination.id)

await ctx.replyWithMarkdown(promptWhen, whenKeys)
return next() // await next message
}

let when = await ctx.storage.getData('when')
if (!when) {
when = await parseWhen(ctx.message.text, ctx)
if (!when) return next() // await next message
await ctx.storage.putData('when', when)
}

// clear session data
await Promise.all([
ctx.storage.putData('origin', null),
ctx.storage.putData('destination', null),
ctx.storage.putData('when', null)
])

// fetch & render journeys
await ctx.replyWithChatAction('typing')
const journeys = await hafas.journeys(origin.id, destination.id, {when})
for (let j of journeys.length) {
await ctx.replyWithMarkdown(renderJourney(j), commandKeys)
}

next()
}

module.exports = journeys
57 changes: 57 additions & 0 deletions commands/journeys/render.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict'

const {DateTime} = require('luxon')

const TIMEZONE = process.env.TIMEZONE
if (!TIMEZONE) {
console.error('Missing TIMEZONE env var.')
process.exit(1)
}
const LOCALE = process.env.LOCALE
if (!LOCALE) {
console.error('Missing LOCALE env var.')
process.exit(1)
}

const renderCoords = l => '`' + l.latitude + '`|`' + l.latitude + '`'

const renderTime = (when) => {
return DateTime.fromMillis(+new Date(when), {
locale: LOCALE,
zone: TIMEZONE
}).toLocaleString(DateTime.TIME_SIMPLE)
}

const renderJourney = (j) => {
const dur = new Date(part.arrival) - new Date(part.departure)
let str = [
'From',
j.origin ? j.origin.name : renderCoords(j.origin),
'to',
j.destination ? j.destination.name : renderCoords(j.destination),
'in',
ms(dur)
].join(' ') + '.\n'

for (let i = 0; i < j.legs.length; i++) {
const leg = j.legs[i]

// todo: emoji for line
// todo: links/commands for origin & destination
str += `\n${renderTime(part.departure)} – *${leg.origin.name}*\n`

const dur = new Date(part.arrival) - new Date(part.departure)
if (leg.mode === 'walking') str += '*walk*'
else if (leg.line) str += `with *${leg.line.name}* to *${leg.direction}*`
else str += leg.mode
str += ' for ' + ms(dur)

if (i === (j.legs.length - 1)) {
str += `\n${renderTime(leg.arrival)} – *${leg.destination.name}*`
}
}

return str
}

module.exports = renderJourney
13 changes: 13 additions & 0 deletions lib/find-station.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict'

const searchStations = require('vbb-stations-autocomplete')
const allStations = require('vbb-stations/simple')

const findStation = (name) => {
let [station] = searchStations(name, 1, false, false) // non-fuzzy
if (!station) [station] = searchStations(name, 1, true, false) // fuzzy
if (station) station = allStations.find(s => s.id === station.id) // get details
return station
}

module.exports = findStation
5 changes: 2 additions & 3 deletions lib/frequent-stations-keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ const Markup = require('telegraf/markup')
const namesById = Object.create(null)
for (let station of stations) namesById[station.id] = station.name

const getFrequentStationsKeyboard = (ids) => {
const keys = []
const getFrequentStationsKeyboard = (ids, keys = []) => {
for (let id of ids) {
if (id in namesById) keys.push(namesById[id])
if (id in namesById) keys.push(Markup.button(namesById[id]))
}
return Markup.keyboard(keys).oneTime().extra()
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"hifo": "^1.0.0",
"js-string-escape": "^1.0.1",
"level": "^3.0.0",
"luxon": "^0.5.3",
"ms": "^2.1.1",
"parse-messy-time": "^2.1.0",
"string-width": "^2.1.1",
Expand Down

0 comments on commit 0933f2f

Please sign in to comment.