I am new to PusherJS and developing a real-time chat app using PHP. Everything working fine but when I press enter to send message, it append it in sender's box and into receiver's box also. How I can differentiate it on the basis of session or user_id. See there are same message is sender and receiver's box:
Real-Time.php:
$options = array(
'cluster' => 'ap2',
'useTLS' => true
);
$pusher = new Pusher\Pusher(
'c575a7********edb87d',
'8fee27********57fdd2',
'7***6*',
$options
);
$pusher->trigger('channel', 'event', $data);
The .js file:
var pusher = new Pusher('c575a76********db87d', {
cluster: 'ap2',
forceTLS: true
});
var channel = pusher.subscribe('channel');
channel.bind('event', function(data) {
var msg_template = ``; //<-- Just removed template it's simple HTML
$("ul#messages").append(msg_template);
});
You need to restrict the message sender from receiving the broadcasted message. How to do so is documented at on the Pusher website.
First, you need to store the SocketID when connecting to Channels:
var pusher = new Pusher('APP_KEY');
var socketId = null;
pusher.connection.bind('connected', function() {
socketId = pusher.connection.socket_id;
});
Once this has been found, you can use it in the event sent to the server:
data: {
id: 'some_id',
updated_value: 'some_value',
socket_id: socketId // pass socket_id parameter to be used by server
}
This will then prevent the client with that socketId from receiving the message.
Related
so i am trying to get this test where i get each object i create, broadcasted without refreshing the page.
Here is the js where we instantiate pusher and bind the event for UserHasRegistered:
// Enable pusher logging - don't include this in production
Pusher.logToConsole = true;
(function() {
var pusher = new Pusher('3c50750503376de6d201', {
cluster: 'eu',
encrypted: true
});
var channel = pusher.subscribe('test');
channel.bind('App\\Events\\UserHasRegistered', function(data) {
console.log(data);
});
})();
</script>
This is the route where i broadcast a registered user name, wich i also mention in the binding to pusher.
Route::get('broadcast', function(){
$name = Request::input('name');
event(new UserHasRegistered($name));
return 'Done';
});
The event Listener is working, i got the env setup as well as the broadcast.php, but this is what i get:
Pusher : State changed : initialized -> connecting pusher.min.js:8:2561
Pusher : Connecting : {"transport":"ws","url":"wss://ws-eu.pusher.com:443 /app/3c50750503376de6d201?protocol=7&client=js&version=4.1.0&flash=false"} pusher.min.js:8:2561
Pusher : State changed : connecting -> connected with new socket ID 123754.768449 pusher.min.js:8:2561
Pusher : Event sent : {"event":"pusher:subscribe","data":{"channel":"test"}} pusher.min.js:8:2561
Pusher : Event recd : {"event":"pusher_internal:subscription_succeeded","data":{},"channel":"test"} pusher.min.js:8:2561
Pusher : No callbacks on test for pusher:subscription_succeeded pusher.min.js:8:2561
You should use both. To catch the error and subscribe to your channel:
channel.bind('pusher:subscription_succeeded', function(members) {
// alert('successfully subscribed!');
});
channel.bind("App\\Events\\UserHasRegistered", function(data) {
console.log(data);
});
Since you can bind to more than one event.
I want to save the chat history in the database. Is there any inbuilt functions for that ?
chat.controller('chat', ['Messages', '$scope', function(Messages, $scope) {
// Message Inbox
$scope.chats = {};
Messages.user({id: "support-agent", name: "Support Agent"});
// Receive Messages
Messages.receive(function(message, isPrivate) {
// isPrivate is always true
// create a new chat if doesn't exist
if(!$scope.chats[message.user.id]) {
$scope.chats[message.user.id] = {
user: message.user,
messages: []
};
}
// add messages to the chat
$scope.chats[message.user.id].messages.push(message);
});
// Send Messages
$scope.send = function(to, text) {
var message = {
to: to,
data: text,
user: Messages.user()
};
Messages.send(message);
// because we are sending a message to a user's personal channel,
// but not subscribing to it we need to keep track of sent messages
// ourselves
$scope.chats[to].messages.push(message);
};
}]);
Where to add the history function in the code ?
I am trying to test a basic pusher trigger event that gives me a simple alert and a console log, but it wont work. The response is received from the pusher console debug but there are no alerts. I am using laravel 5.3 and its routes and a view for this. Below is my code. I have censored the sensitive information.
route file web.php
Route::get('/bridge', function() {
error_reporting(E_ALL);
$options = array(
'cluster' => 'ap1',
'encrypted' => true
);
$pusher = new Pusher(
'key censored',
'secret censored',
'app id censore',
$options
);
$data['message'] = 'hello world';
$pusher->trigger('test_channel', 'my_event', $data);
return view('pusher');
});
and view pusher.blade.php
<!DOCTYPE html>
<head>
<title>Pusher Test</title>
<script src="https://js.pusher.com/3.2/pusher.min.js"></script>
<script>
// Enable pusher logging - don't include this in production
Pusher.logToConsole = true;
var pusher = new Pusher('e5bbf707214a6223d044', {
cluster: 'ap1',
encrypted: true
});
var channel = pusher.subscribe('test_channel');
channel.bind('my_event', function(data) {
alert(data);
console.log(data);
});
</script>
</head>
chrome console gives me the following logs.
Pusher : State changed : initialized -> connecting
Pusher : Connecting : {"transport":"ws","url":"wss://ws-ap1.pusher.com:443/app/censored key?protocol=7&client=js&version=3.2.2&flash=false"}
Pusher : State changed : connecting -> connected with new socket ID 5034.8700909
Pusher : Event sent : {"event":"pusher:subscribe","data":{"channel":"test_channel"}}
Pusher : Event recd : {"event":"pusher_internal:subscription_succeeded","data":{},"channel":"test_channel"}
Pusher : No callbacks on test_channel for pusher:subscription_succeeded
You created a binding for the my_event event. This error is complaining that you don't have a callback for the pusher:subscription_succeeded event. If you want to catch that and handle it, you need to create a binding.
https://pusher.com/docs/client_api_guide/client_presence_channels#pusher-subscription-succeeded
channel.bind('pusher:subscription_succeeded', function(members) {
alert('successfully subscribed!');
});
This worked for me
channel.bind('pusher:subscription_succeeded', function(members) {
// alert('successfully subscribed!');
});
channel.bind("App\\Events\\NewMessage", function(data) {
console.log(data);
});
I need to test a protractor test case in which a user signs up, receives an email, goes to the link provided in the email and fills up his/her details in activation signup form.
The problem is how can I get the redeem token from the email. My email has a link to the activation page which has the auth token like following:
http://127.0.0.1:3000/#/signup/redeem/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJlOTRhYzY3MC1kYTNlLTQyYTUtODVkZS02NDU4ZjVmZGMwYjAiLCJzdWIiOiJ0ZXN0QGNvZWYuY28iLCJpYXQiOjE0Mjc0OTM5MDMsImV4cCI6MTQyODA5ODcwM30.
But how do I fetch that token so that I can build the url or how can I click that button in my email so that I can complete the flow ? I am using mailcatcher to simulate email.
This is something I've solved recently. Hope the solution would also apply for your use-case.
Prerequisites:
mail-listener2 package
understanding of the concept of promises
Step by step instructions:
Install mail-listener2:
npm install mail-listener2 --save-dev
In your protractor config initialize Mail Listener and make it available globally:
onPrepare: function () {
var MailListener = require("mail-listener2");
// here goes your email connection configuration
var mailListener = new MailListener({
username: "imap-username",
password: "imap-password",
host: "imap-host",
port: 993, // imap port
tls: true,
tlsOptions: { rejectUnauthorized: false },
mailbox: "INBOX", // mailbox to monitor
searchFilter: ["UNSEEN", "FLAGGED"], // the search filter being used after an IDLE notification has been retrieved
markSeen: true, // all fetched email willbe marked as seen and not fetched next time
fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`,
mailParserOptions: {streamAttachments: true}, // options to be passed to mailParser lib.
attachments: true, // download attachments as they are encountered to the project directory
attachmentOptions: { directory: "attachments/" } // specify a download directory for attachments
});
mailListener.start();
mailListener.on("server:connected", function(){
console.log("Mail listener initialized");
});
global.mailListener = mailListener;
}),
onCleanUp: function () {
mailListener.stop();
},
Create a helper getLastEmail() function which would wait for an email to be retrieved:
function getLastEmail() {
var deferred = protractor.promise.defer();
console.log("Waiting for an email...");
mailListener.on("mail", function(mail){
deferred.fulfill(mail);
});
return deferred.promise;
};
Example test case:
describe("Sample test case", function () {
beforeEach(function () {
browser.get("/#login");
browser.waitForAngular();
});
it("should login with a registration code sent to an email", function () {
element(by.id("username")).sendKeys("MyUserName");
element(by.id("password")).sendKeys("MyPassword");
element(by.id("loginButton")).click();
browser.controlFlow().await(getLastEmail()).then(function (email) {
expect(email.subject).toEqual("New Registration Code");
expect(email.headers.to).toEqual("myemail#email.com");
// extract registration code from the email message
var pattern = /Registration code is: (\w+)/g;
var regCode = pattern.exec(email.text)[1];
console.log(regCode);
});
});
});
The solution I implemented was using mailcatcher API, if you scroll down a bit you'll find the following about the API:
A fairly RESTful URL schema means you can download a list of messages
in JSON from /messages, each message's metadata with
/messages/:id.json, and then the pertinent parts with
/messages/:id.html and /messages/:id.plain for the default HTML and
plain text version, /messages/:id/:cid for individual attachments by
CID, or the whole message with /messages/:id.source.
So we first fetched the whole json response, parse it and fetch the latest email id:
// Returns the last email id
function(emails, user) {
var email, recipient;
for(var i = emails.length - 1; i >= 0; i--) {
email = emails[i];
for(var j = 0; j < email.recipients.length ; j++) {
recipient = email.recipients[j];
if(recipient == "<"+user+">") {
return email.id;
}
}
}
};
using that email id we can get the body of the email by hitting /messages/:id.plain(of course there are more variants like fetching the email source code or email rendered html, we only needed the message) then we can just parse the body to fetch what we want, following is the code:
browser.driver.get(mailcatcherUrl+"/messages");
browser.driver.findElement(by.tagName('body')).getText().then(function(response) {
var emails, lastEmailId, partialTokens ;
emails = JSON.parse(response);
lastEmailId = getLastEmailId(emails, user);
browser.driver.get(mailcatcherUrl+"/messages/"+lastEmailId+".plain");
browser.driver.findElement(by.tagName('body')).getText().then(function(lastEmail) {
// use latestEmail to get what you want.
});
});
And Cheers!
I had to do the same thing but the mail testing server we were using did not have imap support.
So in case anyone runs into the same issue, I achieved a similar solution as alecxe using mailpop3 npm library.
The thing with the pop3 client, however, was that it doesn't act as a listener so we had to define a helper function that would connect, login and fetch the latest email when we needed to test the latest email.
Something like this:
function getLastEmail() {
var deferred = protractor.promise.defer();
var POP3Client = require("mailpop3");
var client = new POP3Client(port, host, {
tlserrs: false,
enabletls: true,
debug: false
});
client.on("connect", function() {
console.log("CONNECT success");
client.login(username, password);
});
client.on("login", function(status, rawdata) {
if (status) {
console.log("LOGIN/PASS success");
client.retr(1);
} else {
console.log("LOGIN/PASS failed");
client.quit();
}
});
client.on("retr", function(status, msgnumber, data, rawdata) {
if (status === true) {
console.log("RETR success for msgnumber " + msgnumber);
deferred.fulfill(data);
} else {
console.log("RETR failed for msgnumber " + msgnumber);
}
client.quit();
});
return deferred.promise;
}
I have a code with subscribe private channels, and when I try make a subscription I have the next message:
Pusher : Couldn't get auth info from your webapp : 404
Scenario:
Javascript(Sencha touch) and PHP(Laravel)
The subscription is in javascript:
Pusher.channel_auth_endpoint = "/pusher.php";
var APP_KEY = '4324523452435234523';
var pusher = new Pusher(APP_KEY);
var channel = pusher.subscribe('private-l2');
channel.bind('pusher:subscription_succeeded', function() {
alert("ahora siiii");
});
// for debugging purposes. Not required.
Pusher.log = function(msg) {
if(window.console && window.console.log) {
window.console.log("PUSHER LOG: "+msg);
}
}
AND the pusher.php / LARAVEL
$this->app_id = '66981';
$this->app_key = '4324523452435234523';
$this->app_secret = 'f34632459911e2670dcf';
$pusher = new Pusher($this->app_key, $this->app_secret, $this->app_id);
$auth = $pusher->socket_auth(Input::get('channel_name'), Input::get('socket_id'));
echo $auth;
The result is the error:
Pusher : State changed : connecting -> connected
Pusher : Couldn't get auth info from your webapp : 404
You should set up a route for the Pusher authentication
Route::post('pusher/auth', 'ApiController#pusherAuth');
In that method you should first disable php debugbar (if you're using it) authenticate the user and if authentication checks, then return the response.
I'll paste my controller code below.
public function pusherAuth()
{
\Debugbar::disable();
$user = auth()->user();
if ($user) {
$pusher = new \Pusher(config('broadcasting.connections.pusher.key'), config('broadcasting.connections.pusher.secret'), config('broadcasting.connections.pusher.app_id'));
echo $pusher->socket_auth(request()->input('channel_name'), request()->input('socket_id'));
return;
}else {
header('', true, 403);
echo "Forbidden";
return;
}
}
My JS code:
var pusher = new Pusher(project.pusherKey, {
cluster: 'eu',
encrypted: true,
authEndpoint: apiUrl(['pusher', 'auth']), // just a helper method to create a link
auth: {
headers: {
'X-CSRF-Token': project.token // CSRF token
}
}
});
var channelName = 'private-notifications-' + project.userId; // channel for the user id
var channel = pusher.subscribe(channelName);
channel.bind('new_notification', function (data)
{
app.addNotification(data); // send the notification in the JS app
});
I hope this helps.
Cheers!
Private Pusher channels require the client to authenticate for access. See http://pusher.com/docs/authenticating_users for details on configuring the client for authentication and setting up an authentication endpoint.
Change
Pusher.channel_auth_endpoint = "/pusher.php";
for:
Pusher.channel_auth_endpoint = "/public/broadcasting/auth";
I am not expert at laravel but I guess you have used get request to retrieve data(Socket id & channel name) while it's the post request from pusher server to your server endpoint. Use post to retrieve the data.