<?php
							 | 
						|
								use \Firebase\JWT\JWT;
							 | 
						|
								
							 | 
						|
								$app->get('/alexa', function() use($app) {
							 | 
						|
								  render('alexa', array(
							 | 
						|
								    'title' => 'Teacup for Alexa'
							 | 
						|
								  ));
							 | 
						|
								});
							 | 
						|
								
							 | 
						|
								$app->get('/alexa/auth', function() use($app) {
							 | 
						|
								  $req = $app->request();
							 | 
						|
								  $params = $req->params();
							 | 
						|
								
							 | 
						|
								  $required = ['client_id', 'response_type', 'state', 'redirect_uri'];
							 | 
						|
								  $params_present = array_keys($params);
							 | 
						|
								
							 | 
						|
								  // Validate Alexa OAuth parameters
							 | 
						|
								  if(count(array_intersect($required, $params_present)) != count($required)) {
							 | 
						|
								    render('auth_error', array(
							 | 
						|
								      'title' => 'Sign In',
							 | 
						|
								      'error' => 'Missing parameters',
							 | 
						|
								      'errorDescription' => 'One or more required parameters were missing',
							 | 
						|
								      'footer' => false
							 | 
						|
								    ));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // Check that redirect URI is one that is allowed
							 | 
						|
								  if(!in_array($params['redirect_uri'], Config::$alexaRedirectURIs)) {
							 | 
						|
								    render('auth_error', array(
							 | 
						|
								      'title' => 'Sign In',
							 | 
						|
								      'error' => 'Invalid redirect URI',
							 | 
						|
								      'errorDescription' => 'Alexa sent an invalid redirect URI',
							 | 
						|
								      'footer' => false
							 | 
						|
								    ));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if($params['client_id'] != Config::$alexaClientID) {
							 | 
						|
								    render('auth_error', array(
							 | 
						|
								      'title' => 'Sign In',
							 | 
						|
								      'error' => 'Invalid Client ID',
							 | 
						|
								      'errorDescription' => 'Alexa sent an invalid client ID',
							 | 
						|
								      'footer' => false
							 | 
						|
								    ));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // Pass through the OAuth parameters
							 | 
						|
								  render('alexa-auth', [
							 | 
						|
								    'title' => 'Teacup for Alexa',
							 | 
						|
								    'client_id' => $params['client_id'],
							 | 
						|
								    'response_type' => $params['response_type'],
							 | 
						|
								    'state' => $params['state'],
							 | 
						|
								    'redirect_uri' => $params['redirect_uri'],
							 | 
						|
								    'footer' => false
							 | 
						|
								  ]);
							 | 
						|
								});
							 | 
						|
								
							 | 
						|
								$app->post('/alexa/login', function() use($app) {
							 | 
						|
								  $req = $app->request();
							 | 
						|
								  $params = $req->params();
							 | 
						|
								
							 | 
						|
								  $required = ['code', 'client_id', 'state', 'redirect_uri'];
							 | 
						|
								  $params_present = array_keys($params);
							 | 
						|
								
							 | 
						|
								  if(count(array_intersect($required, $params_present)) != count($required)) {
							 | 
						|
								    render('auth_error', array(
							 | 
						|
								      'title' => 'Sign In',
							 | 
						|
								      'error' => 'Missing parameters',
							 | 
						|
								      'errorDescription' => 'One or more required parameters were missing',
							 | 
						|
								      'footer' => false
							 | 
						|
								    ));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  $user = ORM::for_table('users')
							 | 
						|
								    ->where('device_code', $params['code'])
							 | 
						|
								    ->where_gt('device_code_expires', date('Y-m-d H:i:s'))->find_one();
							 | 
						|
								
							 | 
						|
								  if(!$user) {
							 | 
						|
								    render('auth_error', array(
							 | 
						|
								      'title' => 'Sign In',
							 | 
						|
								      'error' => 'Invalid code',
							 | 
						|
								      'errorDescription' => 'The code you entered is invalid or has expired',
							 | 
						|
								      'footer' => false
							 | 
						|
								    ));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  $code = JWT::encode(array(
							 | 
						|
								    'user_id' => $user->id,
							 | 
						|
								    'iat' => time(),
							 | 
						|
								    'exp' => time()+300,
							 | 
						|
								    'client_id' => $params['client_id'],
							 | 
						|
								    'state' => $params['state'],
							 | 
						|
								    'redirect_uri' => $params['redirect_uri'],
							 | 
						|
								  ), Config::$jwtSecret);
							 | 
						|
								
							 | 
						|
								  $redirect = $params['redirect_uri'] . '?code=' . $code . '&state=' . $params['state'];
							 | 
						|
								
							 | 
						|
								  $app->redirect($redirect, 302);
							 | 
						|
								});
							 | 
						|
								
							 | 
						|
								$app->post('/alexa/token', function() use($app) {
							 | 
						|
								  $req = $app->request();
							 | 
						|
								  $params = $req->params();
							 | 
						|
								  // Alexa requests a token given a code generated above
							 | 
						|
								
							 | 
						|
								  // Verify the client ID and secret
							 | 
						|
								  if($params['client_id'] != Config::$alexaClientID 
							 | 
						|
								    || $params['client_secret'] != Config::$alexaClientSecret) {
							 | 
						|
								    $app->response->setStatus(400);
							 | 
						|
								    $app->response()['Content-type'] = 'application/json';
							 | 
						|
								    $app->response()->body(json_encode([
							 | 
						|
								      'error' => 'forbidden',
							 | 
						|
								      'error_description' => 'The client ID and secret do not match'
							 | 
						|
								    ]));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if(array_key_exists('code', $params)) {
							 | 
						|
								    $jwt = $params['code'];
							 | 
						|
								  } elseif(array_key_exists('refresh_token', $params)) {
							 | 
						|
								    $jwt = $params['refresh_token'];
							 | 
						|
								  } else {
							 | 
						|
								    $app->response->setStatus(400);
							 | 
						|
								    $app->response()['Content-type'] = 'application/json';
							 | 
						|
								    $app->response()->body(json_encode([
							 | 
						|
								      'error' => 'bad_request',
							 | 
						|
								      'error_description' => 'Must provide either an authorization code or refresh token'
							 | 
						|
								    ]));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // Validate the JWT
							 | 
						|
								  try {
							 | 
						|
								    $user = JWT::decode($jwt, Config::$jwtSecret, ['HS256']);
							 | 
						|
								  } catch(Exception $e) {
							 | 
						|
								    $app->response->setStatus(400);
							 | 
						|
								    $app->response()['Content-type'] = 'application/json';
							 | 
						|
								    $app->response()->body(json_encode([
							 | 
						|
								      'error' => 'unauthorized',
							 | 
						|
								      'error_description' => 'The authorization code or refresh token was invalid'
							 | 
						|
								    ]));
							 | 
						|
								    return;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // Generate an access token and refresh token
							 | 
						|
								  $access_token = JWT::encode([
							 | 
						|
								    'user_id' => $user->user_id,
							 | 
						|
								    'client_id' => $user->client_id,
							 | 
						|
								    'iat' => time(),
							 | 
						|
								  ], Config::$jwtSecret);
							 | 
						|
								  $refresh_token = JWT::encode([
							 | 
						|
								    'user_id' => $user->user_id,
							 | 
						|
								    'client_id' => $user->client_id,
							 | 
						|
								    'iat' => time(),
							 | 
						|
								  ], Config::$jwtSecret);
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								  $app->response()['Content-type'] = 'application/json';
							 | 
						|
								  $app->response()->body(json_encode([
							 | 
						|
								    'access_token' => $access_token,
							 | 
						|
								    'refresh_token' => $refresh_token
							 | 
						|
								  ]));
							 | 
						|
								});
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								$app->post('/alexa/endpoint', function() use($app) {
							 | 
						|
								
							 | 
						|
								  $input = file_get_contents('php://input');
							 | 
						|
								  $json = json_decode($input, 'input');
							 | 
						|
								
							 | 
						|
								  $alexaRequest = \Alexa\Request\Request::fromData($json);
							 | 
						|
								
							 | 
						|
								  if($alexaRequest instanceof Alexa\Request\IntentRequest) {
							 | 
						|
								
							 | 
						|
								    # Verify the access token
							 | 
						|
								    try {
							 | 
						|
								      $data = JWT::decode($alexaRequest->user->accessToken, Config::$jwtSecret, ['HS256']);
							 | 
						|
								    } catch(Exception $e) {
							 | 
						|
								      $app->response->setStatus(401);
							 | 
						|
								      $app->response()['Content-type'] = 'application/json';
							 | 
						|
								      $app->response()->body(json_encode([
							 | 
						|
								        'error' => 'unauthorized',
							 | 
						|
								        'error_description' => 'The access token was invalid or has expired'
							 | 
						|
								      ]));
							 | 
						|
								      return;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    $user = ORM::for_table('users')->find_one($data->user_id);
							 | 
						|
								
							 | 
						|
								    if(!$user) {
							 | 
						|
								      $app->response->setStatus(400);
							 | 
						|
								      return;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    $action = $alexaRequest->slots['Action'];
							 | 
						|
								    $action = ($action == 'drank' ? 'drank' : 'ate');
							 | 
						|
								    $food = ucfirst($alexaRequest->slots['Food']);
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								    $entry = ORM::for_table('entries')->create();
							 | 
						|
								    $entry->user_id = $user->id;
							 | 
						|
								    $entry->type = ($action == 'drank' ? 'drink' : 'eat');
							 | 
						|
								    $entry->content = $food;
							 | 
						|
								    $entry->published = date('Y-m-d H:i:s');
							 | 
						|
								    $entry->save();
							 | 
						|
								
							 | 
						|
								    $text_content = 'Just ' . $action . ': ' . $food;
							 | 
						|
								
							 | 
						|
								    if($user->micropub_endpoint) {
							 | 
						|
								      $mp_request = array(
							 | 
						|
								        'h' => 'entry',
							 | 
						|
								        'published' => date('Y-m-d H:i:s'),
							 | 
						|
								        'summary' => $text_content
							 | 
						|
								      );
							 | 
						|
								      if($user->enable_array_micropub) {
							 | 
						|
								        $mp_request[$action] = [
							 | 
						|
								          'type' => 'h-food',
							 | 
						|
								          'properties' => [
							 | 
						|
								            'name' => $food
							 | 
						|
								          ]
							 | 
						|
								        ];
							 | 
						|
								      } else {
							 | 
						|
								        $mp_request['p3k-food'] = $food;
							 | 
						|
								        $mp_request['p3k-type'] = $entry->type;
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      $r = micropub_post($user->micropub_endpoint, $mp_request, $user->access_token);
							 | 
						|
								      $request = $r['request'];
							 | 
						|
								      $response = $r['response'];
							 | 
						|
								
							 | 
						|
								      $entry->micropub_response = $response;
							 | 
						|
								      if($response && preg_match('/Location: (.+)/', $response, $match)) {
							 | 
						|
								        $url = $match[1];
							 | 
						|
								        $entry->micropub_success = 1;
							 | 
						|
								        $entry->canonical_url = $url;
							 | 
						|
								      } else {
							 | 
						|
								        $entry->micropub_success = 0;
							 | 
						|
								        $url = Config::$base_url . $user->url . '/' . $entry->id;
							 | 
						|
								      }
							 | 
						|
								      $entry->save();
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								    $response = new \Alexa\Response\Response;
							 | 
						|
								    $response->respond('Got it!')
							 | 
						|
								      ->withCard('You '.$action.': '.$food, $url)
							 | 
						|
								      ->endSession(true);
							 | 
						|
								
							 | 
						|
								    $app->response()['Content-type'] = 'application/json';
							 | 
						|
								    $app->response()->body(json_encode($response->render()));
							 | 
						|
								  }
							 | 
						|
								});
							 |