I downloaded the PHP quickstart from google (link) and put it online on a secure site.
When subscribing to timeline events, the file notify.php should be called. However, this never happens. For example when removing a timeline card I expect notify.php to be called, but it is not.
I really don't know how to dive deeper into this. Any idea someone?
-- edit --
Some more info on the issue (it was a long day yesterday..) ;)
The subscription is being set, when printing the subscription I see that the redirect url is correct:
$subscriptions = $mirror_service->subscriptions->listSubscriptions();
echo '<pre>';
print_r($subscriptions);
echo '</pre>';
results in:
Google_SubscriptionsListResponse Object
(
[__itemsType:protected] => Google_Subscription
[__itemsDataType:protected] => array
[items] => Array
(
[0] => Google_Subscription Object
(
[callbackUrl] => https://url/to/notify.php
[collection] => timeline
[id] => timeline
[kind] => mirror#subscription
[__notificationType:protected] => Google_Notification
[__notificationDataType:protected] =>
[notification] =>
[operation] =>
[updated] => 2014-03-21T09:34:45.391Z
[userToken] => 108736261363015154260
[verifyToken] =>
)
)
[kind] => mirror#subscriptionsList
)
In order to check if notify.php is called I'm creating a "log file" in a writable folder on the server:
<?php
// all the way on top of notify.php
$file = __DIR__ .'/db/log.txt';
function logfile($txt) {
global $file;
$str = is_file($file)? file_get_contents($file) : '';
file_put_contents($file, $str ."\n". $txt);
}
logfile('--- notify.php is being run ---');
//..
The logfile is written when I request https://url/to/notify.php, but the file is not written when I expect the subscription callback to fire.
Also the url https://url/to/notify.php has been added to the redirect URIs in the Google Developer Console, along with the oauth2callback.php
Ok I got it fixed.. Or actually, nothing was wrong..
I assumed that I would get notifications of newly created and deleted timeline items. Believe I read this somewhere.
However, today a collegue walked in who actually owns a glass device (the only one in the company). When I started playing with the menu items (you can't use them in the quickstart webapp) I could reply to a card and: notify.php was actually called!
Too bad this isn't well documented in the quickstart app itself.. But I am happy now :)
You should check to make sure that the subscription is actually created and contains the callback URL that you expect it to be.
Take a look at mirror-client.php:subscribe_to_notifications() and the "insertSubscriptions" case in index.php to verify the URL of the subscription.
You may also wish to add this function (from https://developers.google.com/glass/v1/reference/subscriptions/list) which will list your subscriptions. You can then use this to determine what URL is stored and verify that URL is a secure working one.
function printSubscriptions($service) {
try {
$subscriptions = $service->subscriptions->listSubscriptions();
foreach ($subscriptions->getItems() as $subscription) {
print 'Collection: ' . $subscription->getCollection();
print 'User token: ' . $subscription->getUserToken();
print 'Verify token: ' . $subscription->getVerifyToken();
print 'Callback URL: ' . $subscription->getCallbackUrl();
if ($subscription->getOperation()) {
print 'Operation:';
foreach ($subscription->getOperation() as $operation) {
print ' * ' . $operation;
}
print 'Operation: ALL';
}
}
} catch (Exception $e) {
print 'An error occurred: ' . $e->getMessage();
}
}
Related
Context
I am trying to make webservice that fetches the name and email from an users Apple account and place a Song or Artist in his library.
For adding a Song to the library I found this apple-music-api. library. To make requests on behalf of a user you need to request a user token with Apple MusicKit JS library.
For fetching the name and email of the user I use this oauth2 client that uses the signin with Apple functionality.
Problem
A Using the apple music kit... I can not query any user profile data. At least I cannot seem to find an example nor any documentation of this. Is there a possibility to get the user email and name using this route?
B Using the Sign in with Apple oauth flow I receive an access token which contains the name and email. But I cannot use the token to query the apple music api. It seems their scopes are limited to the name and email...and no scope for the music api or related seems to exist. Is there a possibility to get an user token that can be used on the music api?
C Are there any other possibilities to accomplish this without requiring the user to sign in twice on apple (once for the email and once for pushing the Song to his library)
What I tried for option B
// $leeway is needed for clock skew
Firebase\JWT\JWT::$leeway = 60;
$provider = new League\OAuth2\Client\Provider\Apple([
'clientId' => 'com.myapp.www',
'teamId' => 'team.id', // 1A234BFK46 https://developer.apple.com/account/#/membership/ (Team ID)
'keyFileId' => 'key.id', // 1ABC6523AA https://developer.apple.com/account/resources/authkeys/list (Key ID)
'keyFilePath' => dirname(__FILE__) . '/AuthKey_key.id.p8', // __DIR__ . '/AuthKey_1ABC6523AA.p8' -> Download key above
'redirectUri' => PLUGIN_URL . 'callback-apple-music.php',
]);
if (isset($_POST['code'])) {
if (empty($_POST['state']) || !isset($_COOKIE['apple-oauth2state']) || ($_POST['state'] !== $_SESSION['apple-oauth2state'])) {
unset($_COOKIE['apple-oauth2state']);
exit('Invalid state');
} else {
try {
// Try to get an access token (using the authorization code grant) via signin_with_apple
/** #var AppleAccessToken $token */
$token = $provider->getAccessToken('authorization_code', [
'code' => $_POST['code']
]);
$access_token = $token->getToken();
// create an client for api.music.apple
$tokenGenerator = new PouleR\AppleMusicAPI\AppleMusicAPITokenGenerator();
$jwtToken = $tokenGenerator->generateDeveloperToken(
'team.id',
'key.id',
dirname(__FILE__) .'/AuthKey_key.id.p8'
);
// create a developer token again
$curl = new \Symfony\Component\HttpClient\CurlHttpClient();
$client = new PouleR\AppleMusicAPI\APIClient($curl);
$client->setDeveloperToken($jwtToken);
$api = new PouleR\AppleMusicAPI\AppleMusicAPI($client);
$api->setMusicUserToken($access_token);
// This endpoint needs authorisation
$result = $api->getAllLibraryPlaylists(); //https://api.music.apple.com/v1/me/library/playlists?offset=0&limit=25
echo '<pre>';
print_r($result);
echo '</pre>';
// wp_redirect($redirect_url);
exit;
} catch (Exception $e) {
echo '<pre>';
print_r($e);
echo '</pre>';
}
}
}
The problem with the question is that these are three questions - and not telling which client.
Most commonly "login with" is only good for creating local accounts without much typing.
And it is quite likely intentional, that the oAuth2 scope is extremely limited for this purpose.
And I've looked it up ...one needs a "Music User Token":
https://developer.apple.com/documentation/applemusicapi/getting_keys_and_creating_tokens
And this token needs to be passed as HTTP header: 'Music-User-Token: [music user token]'.
Which means, that the user token may either originate from an iOS device (you'd need to expose eg. a REST API, so that it can be posted and then used by PHP as HTTP header, on the server-side): https://developer.apple.com/documentation/storekit/skcloudservicecontroller/2909079-requestusertoken (this only requires a login to your own API).
When running Apple MusicKit JS on the cient-side (browser), two logins may not be evitable:
https://developer.apple.com/documentation/musickitjs/musickit/musickitinstance/2992701-authorize
It makes no sense to use both of these flows within the same method(which also ignores the principle of single responsibility).
I have setup a Google Actions conversational project, with dialogflow and webhook intents served by my server in PHP.
Upon new dialogflow conversation startup, e.g. just before replying to the welcome intent, the php code is pushing some session entities to the dialogflow engine.
The dialogflow engine recognized correctly the session entity words until two weeks ago, when it suddendly stopped working (I have changed nothing in the code), and right now it's still not working.
The session entities are created with no errors (I added code to query dialogflow api and list session entitites after creation, and google is replying with all the words I send).
However the Intent does not recognize and does not fill in the entity parameters.
Here is the code I'm using to push the entities (using google dialogflow v2 library):
$names = [....array of strings....];
$parent = 'projects/inim-prova/agent/sessions/' . $sessionId;
$client = new SessionEntityTypesClient(['credentials' => $keyfile]]);
$entities = array_map(function($item) { return new Entity(['value' => $item, 'synonyms' => [$item]]); }, array_unique($names));
$entityType = new SessionEntityType([
'name' => $parent . "/entityTypes/$displayName",
'entity_override_mode' => EntityOverrideMode::ENTITY_OVERRIDE_MODE_SUPPLEMENT,
'entities' => $entities
]);
$client->createSessionEntityType($parent, $entityType);
this code runs with no exception thrown.
After a few lines I'm querying the server to see if everything is ok:
$req = $client->listSessionEntityTypes($this->webhookRequest->getSession());
foreach ($req as $element) {
Logger::log(Logger::DEV, __METHOD__, "SessionEntityType: " . $element->getName());
$entities = $element->getEntities();
foreach ($entities as $entity) {
$synonyms = [];
foreach ($entity->getSynonyms() as $synonym) $synonyms[] = $synonym;
Logger::log(Logger::DEV, __METHOD__, ">> " . $entity->getValue() . ": " . implode(', ', $synonyms));
}
}
and this is an extract from the debug log:
SessionEntityType: projects/inim-prova/agent/sessions/ABwppHHhgwxi5OGznpUtUq2D7BQrOKWB5Y5UYr20HRKI14iASKugPw2dL2VKMwfvZ193Mq_DUb2emQ/entityTypes/NomiLuoghiUscite
>> sala: sala
>> cucina: cucina
>> giardino: giardino
SessionEntityType: projects/inim-prova/agent/sessions/ABwppHHhgwxi5OGznpUtUq2D7BQrOKWB5Y5UYr20HRKI14iASKugPw2dL2VKMwfvZ193Mq_DUb2emQ/entityTypes/NomiUscite
>> luci: luci
>> irrigazione: irrigazione
>> cappa: cappa
therefore all the entities seem to be in the right place.
This is the intent training phrase:
(please note that ArticoliDeterminativi and Preposizioni are static entities which I'm ignoring).
and these are the parameters I get on webhook request when I say Accendi le luci in cucina:
'parameters' => array (
'NomiUscite.original' => '',
'Preposizioni.original' => '',
'NomiLuoghiUscite' => '',
'NomiUscite' => '',
'Preposizioni' => '',
'ArticoliDeterminativi.original' => 'le',
'NomiLuoghiUscite.original' => '',
'ArticoliDeterminativi' => 'il',
)
As you can see, NomiUscite and NomiLuoghiUscite are empty. I expect them to be luci and cucina.
I'm really clueless.
It looks like it really was a dialogflow bug, not a flaw in my code.
After a couple of week, Support guys from Google replied me telling that the issue has been resolved.
I've run again tests with no modification on code at all, and now it works.
You should add words 'uscita' and 'luogo' to the Session Entities. I can how the parameters can be empty if you annotate words that are not part of the entity.
You also can try adding 'Accendi le luci in cucina' as a Training Phrase.
Given that I have a WHMCS addon that I call 'my_addon'. I created the main addon file 'my_addon.php' which does contain nothing than:
<?php
function my_addon_clientarea($vars) {
$client = null;
return array(
'pagetitle' => 'My Addon',
'breadcrumb' => array('index.php?m=my_addon'=>'My Addon'),
'templatefile' => 'views/myaddon_view',
'vars' => array(
'client' => $client
)
);
}
This does basically work. It does give me my template file, everything is passed through. My question is: How do I get the currently logged in client from within that function?
I didn't find any API method and I can't see any constant which does hold this information.
There must be a way to get the current client within the clientarea? Thanks for your help!
For those who do come after me and have the same problem: it's easy to solve. Turned out, that I just had to think it through... I found the client id to be available in the $_SESSION-variable.
So, if you are looking for the client's id:
<?php
function my_addon_clientarea($vars) {
$clientid = $_SESSION['uid'];
// And so on...
}
The official way to get current user information is:
$currentUser = new \WHMCS\Authentication\CurrentUser;
$user = $currentUser->user();
You can find more information here
Im new to coding and web development as it is and diving into the deep end with API's is a thing i wish i never had done! However being said i have progressed further than expected. I am now having problems when trying to add custom fields to the add contact feature. Im trying to get the code to add the hidden form input fields when the user hits my thankyou page. I dont want to use Getresponses own Form builder for my main page so it was better to use the API. I have the code running perfectly when it comes to just adding the contact however when i add the set_contact_customs the code does not execute and fails with the following error: (Request have return error: Array) So i understand its to do with the set_contact_customs array however im clueless as to what it is i have done wrong.. Any advice and help is greatly appreciated as i am still learning the basics so picking up on what you experts say is a great learning curve. Thanks.
--- Below is the working version without the set_contact_customs ----
<?php
// Add contact to selected campaign id
try{
$result_contact = $client->add_contact(
$api_key,
array (
'campaign' => 'My-Camp-ID',
'name' => $fullname,
'email' => $emailaddress
)
);
echo "<p style='color: blue; font-size:24px;'>No Errors, Contact and Custom Fields have been added...</p>";
}
catch (Exception $e) {
echo $e->getMessage();
}
?>
--- Here is the code that causes the problems (with set_contact_customs) ----
<?php
// Add contact to selected campaign id
try{
$result_contact = $client->add_contact(
$api_key,
array (
'campaign' => 'My-Camp-ID',
'name' => $fullname,
'email' => $emailaddress
)
);
$result_contact = $client->set_contact_customs(
$api_key,
array(
'Survey Type' => $surveytype,
'Survey Cost' => $surveycost
)
);
echo "<p style='color: blue; font-size:24px;'> Contact Added </p>";
}
catch (Exception $e) {
echo $e->getMessage();
}
?>
API 2 doesn't really exist: in GetResponse they say version "1.5.0 - this is last JSON/RPC version of our API", especially if you were speaking 10 months ago. Now they are preparing to beta-test v3. So I will assume you were speaking about 1.5 and answer about it (I'm not familiar with v3, maybe there it's different).
You must send contact id with set_contact_customs, and you didn't.
When it says, "request error: array", it doesn't relate to your array (even though the problem is in your array, because you don't send in it contact id), they are sending an array as a response with error messages.
I'd love to tell you, where to get the contact id in order to send it, but I'm looking for it myself now. :)
UPDATE:
Ok, I combined it from pieces all over the internet, and now here's the working format.
You don't need to add_contact and then update it, you can do it in one go, adding the 'customs' parameter to the add_contact call (GR say, that we shouldn't expect for the contact to be added immediately, so you might not even get whom to update, if you call that function right away).
The fields for add_contact are described here.
The 'customs' parameter should look differently. Instead of:
array(
'Survey Type' => $surveytype,
'Survey Cost' => $surveycost
)
it should be:
array(
array( 'name' => 'Survey Type', 'content' => $surveytype ),
array( 'name' => 'Survey Cost', 'content' => $surveycost )
)
By the way, from what I tested, - blessedly, you don't need to define in GR UI those custom fields first, whatever you send, will be added or updated (in their limits for the custom field names and values).
I got error, when tried to send one custom field with empty content, when calling add_contact. When I sent it with set_contact_customs, I didn't get any error; I wanted to see, if it would delete the field or field value - it didn't do a thing.
If you still wish to update the existing contact, here's how to send the contact id with the update call:
$result = $client->set_contact_customs(
$api_key, array(
'contact' => $contact_id,
'customs' => $custom_fields_array
)
);
To first find contact id, you should call get_contacts. And since it's been said (I haven't tested it), that in different campaigns contacts with the same email address have different contact id, you should pass both the campaign, and the email with it.
As you can see, campaign can be sent in 'campaigns' parameter (then campaign id, that you got for add_contact, should be used), or in 'get_campaigns' (then the campaign name or even prefix can be used).
Here's the call with campaign id, for your code:
$result = $client->get_contacts(
$api_key, array(
'campaigns' => array( 'My-Camp-ID' ),
'email' => array( 'EQUALS' => $emailaddress )
)
);
To retrieve contact id from get_contacts, do the same as recommended for retrieving campaign id:
$contact_id = array_pop( array_keys( $result ) );
if ( empty( $contact_id ) ) {
//still not ok
}
else {
//you can call set_contact_customs
}
In order for that error message to be more descriptive, instead of just 'Request have return error: Array', open your jsonRPCClient.php, which you most surely include in your file with these GR function calls, and look for the following line:
!is_null($response['error']) => 'Request have return error: ' . $response['error'],
and replace it with the following, at least:
!is_null($response['error']) => 'Request have returned error: ' . var_export($response['error'], true),
Now your code will use the beloved var_export function and if you make a mistake, you will see in your error log something like:
Request have returned error: array (
'message' => 'Invalid params',
'code' => -32602,
)
I dedicate this thorough answer to all those, who helped me endlessly here on StackOverflow, just giving their answers to someone else's questions, sometimes years ago. Thank you! Hopefully my answer will save someone time, efforts, and mood, too. :)
this is my first question to stackoverflow, so if's something wrong please advise..
Well the problem is Facebook real time updates :
I have successfully created subscription to page and it's admin user ( test page and test app created ), user has agreed permissions to manage_page, read_stream, read_friendlist, read_insights,publish_actions and a lot of user's data ( about,email etc etc )
When i query GET to Graph api with edge /subscriptions i get the result :
{
"data": [
{
"object": "user",
"callback_url": "LIVE_CALLBACK_URL",
"fields": [
"feed"
],
"active": true
},
{
"object": "page",
"callback_url": "LIVE_CALLBACK_URL",
"fields": [
"built",
"description",
"email",
"feed",
"location",
"name",
"personal_info",
"written_by"
],
"active": true
}
]
}
So, that's taken care of, after that i searched why Facebook isn't sending me any updates ( which I log to somefile.txt ) So i found out you need to query the Graph api with /tabs to create page tab for my api. When i query that it returned "success" so that's created ( can't find out where it's stored, but it working ... ).
After that i have created some data on my profile, to test and also on the page to see if the Facebook is sending any updates, and it's suddenly send one update that was comment on my user profile post ( shared on timeline ).
The response was :
14:17 05.02.2015
Array
(
[object] => user
[entry] => Array
(
[0] => Array
(
[uid] => ****my_fb_id****
[id] => ****my_fb_id - not_someone_else****
[time] => 1423146339
[changed_fields] => Array
(
[0] => feed
)
)
)
)
So Facebook send's the empty feed, and it's ok, probably some permissions or something else, but after that i have made more posts and ppl commented on it ( on user profile and test page ) but Facebook didn't send any updates, or any call to my web page.
So has anyone facing this problem in the past, and how he solved that ?
The part that is for receiving the update from Facebook is :
$method = $_SERVER['REQUEST_METHOD'];
if(isset($_GET['hub_mode']) && isset($_GET['hub_verify_token'])){
if (trim($method) == 'GET' && trim($_GET['hub_mode']) == 'subscribe' && trim($_GET['hub_verify_token']) == VERIFY_TOKEN) {
print $_GET['hub_challenge'];
}
}
else if ($method == 'POST') {
$updates = json_decode(file_get_contents("php://input"), true);
$file = 'log.txt';
$current = file_get_contents($file);
#$results = print_r($updates,true);
$current .= $results;
file_put_contents($file, $current);
}
I have tried to detail my problem as much i as could... Searched but don't really understand why it's not working.
Apart from using an outdated method as #CBroe outlined, I think you have a misunderstanding: Facebook will not send you the data that has changed, but the object (and it's fields/edges) the change occurred for User object changes.
For Pages, you can receive the changes itself (changed_fields vs. changes fields in the FB request).
See
https://developers.facebook.com/docs/graph-api/real-time-updates/v2.2#receiveupdates
Once a subscription is successfully created, Facebook will make an HTTP POST request to your callback URL every time that there are changes (to the chosen fields or edges).
That means you need to contruct a separate request from the info object you get from Facebook. In your example it would be a request to your user's feed
GET /{user_id}/feed
to query for recent changes.