We are creating a bot that can join the team meeting and it can start the recording as it joins the team meeting. But we are getting this error(Expected not null
Parameter name: client). I am attaching the code below:
when debugger goes to CreateLocalMediaSession() session method then at that method it gives the error.(Expected not null
Parameter name: client)
public async Task<ICall> JoinCallAsync()
{
// A tracking id for logging purposes. Helps identify this call in logs.
var scenarioId = Guid.NewGuid();
var (chatInfo, meetingInfo) = JoinInfo.ParseJoinURL("https://teams.microsoft.com/l/meetup-join/19:meeting_YTI5NDQ2ODQtMmNlNy00YTBhLTg2NTMtYmZmOGIyMzdhMTgw#thread.v2/0?context=%7B%22Tid%22:%22204d6395-ea6c-4e64-abea-e04cd30845e2%22,%22Oid%22:%225a95f69b-70e2-40d3-8b9a-5810ffcc6ec9%22%7D");
var tenantId = (meetingInfo as OrganizerMeetingInfo).Organizer.GetPrimaryIdentity().GetTenantId();
var mediaSession = this.CreateLocalMediaSession(scenarioId);
var joinParams = new JoinMeetingParameters(chatInfo, meetingInfo, mediaSession)
{
TenantId = tenantId,
};
if (!string.IsNullOrWhiteSpace("bot"))
{
// Teams client does not allow changing of one's display name.
// If the display name is specified, we join as an anonymous (guest) user
// with the specified display name. This will put the bot in lobby
// unless lobby bypass is disabled.
joinParams.GuestIdentity = new Identity
{
Id = Guid.NewGuid().ToString(),
DisplayName = "bot",
};
}
var statefulCall = await this.Client.Calls().AddAsync(joinParams, scenarioId).ConfigureAwait(false);
statefulCall.GraphLogger.Info($"Call creation complete: {statefulCall.Id}");
return statefulCall;
}
Code for creating local media session:
private ILocalMediaSession CreateLocalMediaSession(Guid mediaSessionId = default)
{
try
{
// create media session object, this is needed to establish call connections
return this.Client.CreateMediaSession(
new AudioSocketSettings
{
StreamDirections = StreamDirection.Recvonly,
// Note! Currently, the only audio format supported when receiving unmixed audio is Pcm16K
SupportedAudioFormat = AudioFormat.Pcm16K,
ReceiveUnmixedMeetingAudio = true //get the extra buffers for the speakers
},
new VideoSocketSettings
{
StreamDirections = StreamDirection.Inactive
},
mediaSessionId: mediaSessionId);
}
catch (Exception e)
{
_logger.Log(System.Diagnostics.TraceLevel.Error, e.Message);
throw;
}
}
We are creating a bot that can join the team meeting and it can start the recording as it joins the team meeting. But we are getting this error(Expected not null
Parameter name: client). I am attaching the code below:
error facing:
enter image description here
Related
I'd like to fetch all the Stripe Transfers that make up a Payout. I'm following this Stackoverflow post here that says fetch the balance transactions and pass in a payout ID and set the type to "transfer".
In my Stripe dashboard I can see multiple payouts and I'm just copying/pasting different ID's to test this call.
Problem - I keep getting the same error message from Stripe saying "No such payout: 'po_1KJ6pFQ**********YsFVzT4'"
Here's how I'm calling the balance transactions.
var options = new BalanceTransactionListOptions
{
Payout = "po_1KJ6pFQ**********YsFVzT4",
// Type = "transfer",
// Limit = 100,
};
var service = new BalanceTransactionService();
try {
StripeList<BalanceTransaction> balanceTransactions = service.List(options);
foreach(BalanceTransaction balTransaction in balanceTransactions) { // do something }
}
} catch(StripeException ex) {
var e = ex;
}
No such (object) error messages occurs when the object you're attempting to access does not exist on the Stripe account.
By default, the request would be made on the Stripe account whose API key you're using. If you're using Connect and you need to access an object on a connected account, you should use your platform's API key and the Stripe-Account header.
var requestOptions = new RequestOptions();
requestOptions.StripeAccount = "{{CONNECTED_ACCOUNT_ID}}";
var options = new BalanceTransactionListOptions
{
Payout = "po_1KJ6pFQ**********YsFVzT4",
// Type = "transfer",
// Limit = 100,
};
var service = new BalanceTransactionService();
try {
StripeList<BalanceTransaction> balanceTransactions = service.List(options, requestOptions);
foreach(BalanceTransaction balTransaction in balanceTransactions) { // do something }
}
} catch(StripeException ex) {
var e = ex;
}
I want to store the conversation data to the storage account or cosmos DB. By trying this https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-howto-v4-storage?view=azure-bot-service-4.0&tabs=javascript#using-blob-storage
I am able to send the utteranceslog into blob storage. But I want to store end-to-end conversation data which includes data of both users as well as bot responses using javascript.
I tried using saving user state and conversation state but didn't achieve the desired output.
I created a custom logger (based on an old botduilder-samples sample that isn't there anymore) that accomplishes this using TranscriptLoggerMiddleware. I chose CosmosDB instead of Blob Storage because I felt it was easier to store (and retrieve) as a JSON document. But you could tweak this concept to use any DB. Here is what I did.
First, create your custom logger code. As mentioned, I used CosmosDB so you might have to change some things if you're using a different DB. The timing of the activities was creating concurrency issues, so instead of working around that, I'm storing the transcript object locally and overwriting the DB object on each turn. Maybe not the most elegant, but it works. Also, I've found my wait function to be required. Otherwise you only get one side of the conversation. I've been told this type of wait function is not a best practice, but awaiting a promise or other methods of creating a delay did not work for me. Here is the code:
customerLogger.js
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
const { CosmosDbStorage } = require('botbuilder-azure');
const path = require('path');
/**
* CustomLogger, takes in an activity and saves it for the duration of the conversation, writing to an emulator compatible transcript file in the transcriptsPath folder.
*/
class CustomLogger {
/**
* Log an activity to the log file.
* #param activity Activity being logged.
*/
// Set up Cosmos Storage
constructor(appInsightsClient) {
this.transcriptStorage = new CosmosDbStorage({
serviceEndpoint: process.env.COSMOS_SERVICE_ENDPOINT,
authKey: process.env.COSMOS_AUTH_KEY,
databaseId: process.env.DATABASE,
collectionId: 'bot-transcripts'
});
this.conversationLogger = {};
this.appInsightsClient = appInsightsClient;
this.msDelay = 250;
}
async logActivity(activity) {
if (!activity) {
throw new Error('Activity is required.');
}
// Log only if this is type message
if (activity.type === 'message') {
if (activity.attachments) {
var logTextDb = `${activity.from.name}: ${activity.attachments[0].content.text}`;
} else {
var logTextDb = `${activity.from.name}: ${activity.text}`;
}
if (activity.conversation) {
var id = activity.conversation.id;
if (id.indexOf('|') !== -1) {
id = activity.conversation.id.replace(/\|.*/, '');
}
// Get today's date for datestamp
var currentDate = new Date();
var day = currentDate.getDate();
var month = currentDate.getMonth()+1;
var year = currentDate.getFullYear();
var datestamp = year + '-' + month + '-' + day;
var fileName = `${datestamp}_${id}`;
var timestamp = Math.floor(Date.now()/1);
// CosmosDB logging (JK)
if (!(fileName in this.conversationLogger)) {
this.conversationLogger[fileName] = {};
this.conversationLogger[fileName]['botName'] = process.env.BOTNAME;
}
this.conversationLogger[fileName][timestamp] = logTextDb;
let updateObj = {
[fileName]:{
...this.conversationLogger[fileName]
}
}
// Add delay to ensure messages logged sequentially
await this.wait(this.msDelay);
try {
let result = await this.transcriptStorage.write(updateObj);
} catch(err) {
this.appInsightsClient.trackTrace({message: `Logger ${err.name} - ${path.basename(__filename)}`,severity: 3,properties: {'botName': process.env.BOTNAME, 'error':err.message,'callStack':err.stack}});
}
}
}
}
async wait(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
}
}
exports.CustomLogger = CustomLogger;
Now you need to attach this to the botframework adapter in your index.js file. The relevant pieces of code are:
index.js
const { TranscriptLoggerMiddleware } = require('botbuilder');
const { CustomLogger } = require('./helpers/CustomLogger');
//
//Your code to create your adapter, etc.
//
const transcriptLogger = new TranscriptLoggerMiddleware(new CustomLogger(appInsightsClient));
adapter.use(transcriptLogger);
I'm assuming here you already have your index.js file figured out, but if you need any assistance getting that set up and getting the transcript logger to work with it, just let me know.
EDIT: By request, here is what the object looks like in CosmosDB. Normally I would have the "from name" displayed, but because of the way I was testing the bot it came through "undefined".
{
"id": "2020-3-21_IfHK46rZV42KH5g3dIUgKu-j",
"realId": "2020-3-21_IfHK46rZV42KH5g3dIUgKu-j",
"document": {
"botName": "itInnovationBot",
"1584797671549": "Innovation Bot: Hi! I'm the IT Innovation Bot. I can answer questions about the innovation team and capture your innovation ideas. Let me know how I can help!",
"1584797692355": "undefined: Hello",
"1584797692623": "Innovation Bot: Hello.",
"1584797725223": "undefined: Tell me about my team",
"1584797725490": "Innovation Bot: The innovation team is responsible for investigating, incubating, and launching new technologies and applications. The innovation focus areas are:\n\n* Chatbots\n\n* Augmented Reality/Virtual Reality\n\n* Blockchain\n\n* Robotic Process Automation\n\n* AI & Machine Learning\n\nLet me know if you want to learn more about any of these technologies!",
"1584797746279": "undefined: Thanks",
"1584797746531": "Innovation Bot: You're welcome."
},
"_rid": "OsYpALLrTn2TAwAAAAAAAA==",
"_self": "dbs/OsYpAA==/colls/OsYpALLrTn0=/docs/OsYpALLrTn2TAwAAAAAAAA==/",
"_etag": "\"a4008d12-0000-0300-0000-5e7618330000\"",
"_attachments": "attachments/",
"_ts": 1584797747
}
To read the conversation back (even if still in the middle of the conversation), you just create a connector in your bot, recreate the key, and read the file as below (in this case id is passed into my function and is the conversation id):
const transcriptStorage = new CosmosDbStorage({
serviceEndpoint: process.env.COSMOS_SERVICE_ENDPOINT,
authKey: process.env.COSMOS_AUTH_KEY,
databaseId: process.env.DATABASE,
collectionId: 'bot-transcripts',
partitionKey: process.env.BOTNAME
});
// Get today's date for datestamp
var currentDate = new Date();
var day = currentDate.getDate();
var month = currentDate.getMonth()+1;
var year = currentDate.getFullYear();
var datestamp = year + '-' + month + '-' + day;
var filename = `${datestamp}_${id}`;
var transcript = await transcriptStorage.read([filename]);
Stripe.Net v34.16.0 bounces my code on the creation of the checkout session object responding with:
StripeException: No such plan: plan_myPlanId; a similar object exists in live mode, but a test mode key was used to make this request.
I do not see a means in the Stripe Dashboard to designate a given plan as a test plan .I also do not see
anything resembling a mode property.. my code
public async Task<IActionResult> Index()
{
//var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
//user = await _userManager.FindByIdAsync(userId);
StripeConfiguration.ApiKey = "sk_test_mytestkey";
var options = new Stripe.Checkout.SessionCreateOptions
{
PaymentMethodTypes = new List<string> {
"card",
},
SubscriptionData = new Stripe.Checkout.SessionSubscriptionDataOptions
{
Items = new List<SessionSubscriptionDataItemOptions> {
new SessionSubscriptionDataItemOptions {
Plan = "plan_myplanid",
},
},
},
//to do
SuccessUrl = "localhost://home",
CancelUrl = "localhost://home",
//CancelUrl = "https://example.com/cancel",
};
var service = new Stripe.Checkout.SessionService();
Stripe.Checkout.Session session = service.Create(options); //error out here
StripeCheckoutSessionId stripeCheckoutSessionId = new StripeCheckoutSessionId();
stripeCheckoutSessionId.StripeSessionID = session.Id;
return View(stripeCheckoutSessionId);
}
I am referring to Stripe sample code in the .Net tab here: https://stripe.com/docs/payments/checkout/subscriptions/starting
I appreciate your guidance in correcting my errors.
So I tried running this Push notification sample for Xamarin.Android http://azure.microsoft.com/en-us/documentation/articles/partner-xamarin-mobile-services-android-get-started-push/ and after following instructions from the docs - I got it up and running. The insertion of items work absolutely fine however push notification refuses to work.
This is the error I get on Azure for push: Error: 400 - The supplied notification payload is invalid.
Anyone else tried running this sample on their device and tried push notifications? The error isn't doing much to help my case.
The sample is using PushSharp.
I'd appreciate any help. Thanks a bunch!
This is how I send push notification to Google Cloud Messaging from the back-end server.
public async Task<bool> SendNotification(int id, int index, string from, string text, string tag)
{
try
{
var payload = new
{
data = new
{
message = new
{
// this part can be anything you want
id,
index,
from,
text,
when = DateTime.UtcNow.ToString("s") + "Z"
}
}
};
var json = JsonConvert.SerializeObject(payload);
await _hubClient.SendGcmNativeNotificationAsync(json, tag);
return true;
}
catch (ArgumentException ex)
{
// This is expected when an APNS registration doesn't exist.
return false;
}
Then in your app Intent Service, you can parse the JSON "message":
protected override void OnMessage(Context context, Intent intent)
{
var message = intent.Extras.GetString("message");
// message is JSON payload
// { "id":"someid", "index":"1", "text":"some text","from"... }
var json = JObject.Parse(message);
var id = json["id"].ToString();
var index = json["index"].ToString();
var text = json["text"].ToString();
var from = json["from"].ToString();
var when = DateTime.Parse(json["when"].ToString());
// do whatever you want with your values here
}
I'm using the node-xmpp module to connect to a XMPP server and join a group chat. Connecting to the server, setting the presence, joining the room and reading out messages works so far. But I want to receive the userlist of the room too.
The XMPP protocol requires to send a presence stanza when the client enters the room (http://xmpp.org/extensions/xep-0045.html#enter-pres). But how can I now parse it in node?
My code currently looks like this:
var xmpp = require('node-xmpp');
// Create the XMPP Client
var cl = new xmpp.Client({
jid: jid,
password: password,
reconnect: true
});
// Do things when online
cl.on('online', function() {
util.log("We're online!");
// Set client's presence
cl.send(new xmpp.Element('presence', { type: 'available' }).c('show').t('chat'));
cl.send(new xmpp.Element('presence', { to: room_jid+'/'+room_nick }).c('x', { xmlns: 'http://jabber.org/protocol/muc' }).c('history', {seconds: 1}));
// Send keepalive
setInterval(function() {
cl.send(' ');
}, 30000);
cl.on('stanza', function(stanza) {
// always log error stanzas
if (stanza.attrs.type == 'error') {
util.log('[error] ' + stanza);
return;
}
// ignore everything that isn't a room message
if (!stanza.is('message') || !stanza.attrs.type == 'chat') {
return;
}
var body = stanza.getChild('body');
// message without body is probably a topic change
if (!body) {
return;
}
// Extract username
var from, room, _ref;
_ref = stanza.attrs.from.split('/'), room = _ref[0], from = _ref[1];
var message = body.getText();
// Log topics and messages to the console
if(!from) {
util.log('Topic: ' + message);
} else {
util.log('[' + from + ']: ' + message);
}
});
});
I already tried triggering presence by using
if(stanza.is('presence')) {}
within the cl.on('stanza') part but it doesn't work.
UPDATE: I'm describing a new method now which doesn't require the client to send requests.
Background: When the client joins a group chat, the server returns presence stanzas which contain information about the connected users to the group chat.
cl.on('stanza', function(stanza) {
// always log error stanzas
if (stanza.attrs.type == 'error') {
util.log('[error] ' + stanza);
return;
}
if(stanza.is('presence')){
// We are only interested in stanzas with <x> in the payload or it will throw some errors
if(stanza.getChild('x') !== undefined) {
// Deciding what to do based on the xmlns attribute
var _presXmlns = stanza.getChild('x').attrs.xmlns;
switch(_presXmlns) {
// If someone is joining or leaving
case 'http://jabber.org/protocol/muc#user':
// Get the role of joiner/leaver
_presRole = stanza.getChild('x').getChild('item').attrs.role;
// Get the JID of joiner/leaver
_presJID = stanza.getChild('x').getChild('item').attrs.jid;
// Get the nick of joiner/leaver
_presNick = stanza.attrs.from.split('/')[1];
// If it's not none, this user must be joining or changing his nick
if(_presRole !== 'none') {
// We are now handling the data of joinging / nick changing users. I recommend to use an in-memory store like 'dirty' [https://github.com/felixge/node-dirty] to store information of the users currentliy in the group chat.
} else {
// We are now handling the data of leaving users
}
break;
}
return;
}
return;
}
OLD METHOD
I previously described a method how to query the server for current users in the group chat. By maintaining a store where all user traffic (joining, leaving, nick changing) is stored, this is no longer required. However you could still use it to make sure the data is consistent by issues like a presence stanza was not delivered to the client correctly. That's the reason it's still described below:
To request a list with users connected to the room, you need to perform the following actions:
First send a request to the server and ask for the user list:
cl.send(new xmpp.Element('iq', {from: jid, to: room_jid, type: 'get' }).c('query', { xmlns: 'http://jabber.org/protocol/disco#items' }));
then listen for iq-stanzas, parse them and populate an array with the data:
// Catching the requested user list
if(stanza.is('iq')){
// Fetching usernames from return data (data structure: http://xmpp.org/extensions/xep-0045.html#example-12)
var _items = stanza.getChild('query').getChildren('item');
var users = new Array();
for(var i = 0; i<_items.length; i++) {
// We are building an object here to add more data later
users[i] = new Object();
users[i]['name'] = _items[i].attrs.name;
}
console.log(util.inspect(users, {depth: null, colors: true}));
return;
}
This will provide you with a user list. To request unique JIDs you have to probe every user. To keep the list up to date, you should remove users when they leave and add + probe when they join.