question

Jim Lin avatar image
Jim Lin asked Phong Vu answered

C# app that downloads the call recordings.

I am looking for a simple C# that can download call recordings.

I have looked at https://github.com/ringcentral/RingCentral.Net

Set up a sandbox app made a test call and recorded it. But I am lacking in the ability to just have a simple app that downloads to a local folder on the computer it runs on.

Does anyone have something I can use?

call recording
1 |1500 characters needed characters left characters exceeded

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Phong Vu avatar image
Phong Vu answered

I don't see you call the download function. Modify your code as follows

        const string RINGCENTRAL_CLIENTID = "0_m0q2SexxxxxxKNQ";
        const string RINGCENTRAL_CLIENTSECRET = "S1xxxxxxxxxZMiFFHzzzzzrb_sWSjUg";
        const bool RINGCENTRAL_PRODUCTION = false;
 
        const string RINGCENTRAL_USERNAME = "15555555555";
        const string RINGCENTRAL_PASSWORD = "Password123!";
        const string RINGCENTRAL_EXTENSION = "101";
        static RestClient rcsdk;
        static void Main(string[] args)
        {
            // Display the number of command line arguments.
            Console.WriteLine(args.Length);
            rcsdk = new RestClient(RINGCENTRAL_CLIENTID, RINGCENTRAL_CLIENTSECRET, RINGCENTRAL_PRODUCTION);
            rcsdk.Authorize(RINGCENTRAL_USERNAME, RINGCENTRAL_EXTENSION, RINGCENTRAL_PASSWORD).Wait();
            download_call_log_callrecording().Wait();
        }
        static private async Task download_call_log_callrecording()
        {
            var parameters = new ReadCompanyCallLogParameters();
            parameters.dateFrom = "2021-07-05T00:00:00.000Z";
            parameters.view = "Simple";
            parameters.recordingType = "All";

            var path = "recording_content/";
            System.IO.Directory.CreateDirectory(path);
            long timePerApiCall = 6000;

            var resp = await rcsdk.Restapi().Account().CallLog().List(parameters);
            foreach (var record in resp.records)
            {
                Console.WriteLine("-----");
                Console.WriteLine("Call recording: " + record.recording.contentUri);
                var fileName = path + "call_" + record.recording.id + "_recording";
                var start = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
                var result = await rcsdk.Restapi().Account().Recording(record.recording.id).Get();
                if (result.contentType == "audio/mpeg")
                {
                    fileName += ".mp3";
                }
                else // can check if "audio/x-wav"
                {
                    fileName += ".wav";
                }
                var contentUrl = result.contentUri + "?access_token=" + rcsdk.token.access_token;
                WebClient webClient = new WebClient();
                webClient.DownloadFile(contentUrl, fileName);
                Console.WriteLine("Save recording file to the local machine.");
                var end = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
                var delay = (end - start) * 1000;
                Console.WriteLine(delay);
                if (delay < timePerApiCall)
                {
                    Thread.Sleep((int)(timePerApiCall - delay));
                }
            }
        }
 
1 |1500 characters needed characters left characters exceeded

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Phong Vu avatar image
Phong Vu answered

Try this

static private async Task download_call_log_callrecording()
{
    RestClient rc = new RestClient(RINGCENTRAL_CLIENTID, RINGCENTRAL_CLIENTSECRET, RINGCENTRAL_PRODUCTION);
    await rc.Authorize(RINGCENTRAL_USERNAME, RINGCENTRAL_EXTENSION, RINGCENTRAL_PASSWORD);
    var parameters = new ReadCompanyCallLogParameters();
    parameters.dateFrom = "2021-09-05T00:00:00.000Z";
    parameters.view = "Simple";
    parameters.recordingType = "All";

    var path = "recording_content/";
    System.IO.Directory.CreateDirectory(path);
    long timePerApiCall = 6000;

    var resp = await rc.Restapi().Account().CallLog().List(parameters);
    foreach (var record in resp.records)
    {
        Console.WriteLine("-----");
        Console.WriteLine("Call recording: " + record.recording.contentUri);
        var fileName = path + "call_" + record.recording.id + "_recording";
        var start = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
        var result = await rc.Restapi().Account().Recording(record.recording.id).Get();
        if (result.contentType == "audio/mpeg")
        {
            fileName += ".mp3";
        }
        else // can check if "audio/x-wav"
        {
            fileName += ".wav";
        }
        var contentUrl = result.contentUri + "?access_token=" + rc.token.access_token;
        WebClient webClient = new WebClient();
        webClient.DownloadFile(contentUrl, fileName);
        Console.WriteLine("Save recording file to the local machine.");
        var end = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
        var delay = (end - start) * 1000;
        Console.WriteLine(delay);
        if (delay < timePerApiCall)
        {
            Thread.Sleep((int)(timePerApiCall - delay));
        }
    }
}
1 |1500 characters needed characters left characters exceeded

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Jim Lin avatar image
Jim Lin answered Jim Lin published

ok so i got this put together


using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using RingCentral;

namespace Recordings
{
    class Program
    {
        static void Main(string[] args)
        {
            // Display the number of command line arguments.
            Console.WriteLine(args.Length);
        }


        const string RINGCENTRAL_CLIENTID = "0_m0q2SexxxxxxKNQ";
        const string RINGCENTRAL_CLIENTSECRET = "S1xxxxxxxxxZMiFFHzzzzzrb_sWSjUg";
        const bool RINGCENTRAL_PRODUCTION = false;

        const string RINGCENTRAL_USERNAME = "15555555555";
        const string RINGCENTRAL_PASSWORD = "Password123!";
        const string RINGCENTRAL_EXTENSION = "101";

        static private async Task download_call_log_callrecording()
        {
            RestClient rc = new RestClient(RINGCENTRAL_CLIENTID, RINGCENTRAL_CLIENTSECRET, RINGCENTRAL_PRODUCTION);
            await rc.Authorize(RINGCENTRAL_USERNAME, RINGCENTRAL_EXTENSION, RINGCENTRAL_PASSWORD);
            var parameters = new ReadCompanyCallLogParameters();
            parameters.dateFrom = "2021-09-05T00:00:00.000Z";
            parameters.view = "Simple";
            parameters.recordingType = "All";

            var path = "recording_content/";
            System.IO.Directory.CreateDirectory(path);
            long timePerApiCall = 6000;

            var resp = await rc.Restapi().Account().CallLog().List(parameters);
            foreach (var record in resp.records)
            {
                Console.WriteLine("-----");
                Console.WriteLine("Call recording: " + record.recording.contentUri);
                var fileName = path + "call_" + record.recording.id + "_recording";
                var start = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
                var result = await rc.Restapi().Account().Recording(record.recording.id).Get();
                if (result.contentType == "audio/mpeg")
                {
                    fileName += ".mp3";
                }
                else // can check if "audio/x-wav"
                {
                    fileName += ".wav";
                }
                var contentUrl = result.contentUri + "?access_token=" + rc.token.access_token;
                WebClient webClient = new WebClient();
                webClient.DownloadFile(contentUrl, fileName);
                Console.WriteLine("Save recording file to the local machine.");
                var end = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
                var delay = (end - start) * 1000;
                Console.WriteLine(delay);
                if (delay < timePerApiCall)
                {
                    Thread.Sleep((int)(timePerApiCall - delay));
                }
            }
        }
    }
}


When I run it I get this in the Debug output


'Ring_ABS.exe' (CoreCLR: DefaultDomain): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.8\System.Private.CoreLib.dll'.

'Ring_ABS.exe' (CoreCLR: clrhost): Loaded 'C:\Users\mikea\source\repos\Ring_ABS\bin\Debug\netcoreapp3.1\Ring_ABS.dll'. Symbols loaded.

'Ring_ABS.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.8\System.Runtime.dll'.

'Ring_ABS.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.8\System.Console.dll'.

'Ring_ABS.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.8\System.Threading.dll'.

'Ring_ABS.exe' (CoreCLR: clrhost): Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.8\System.Runtime.Extensions.dll'.

The program '[36264] Ring_ABS.exe' has exited with code 0 (0x0).



1 |1500 characters needed characters left characters exceeded

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Jim Lin avatar image
Jim Lin answered

ok it's working great and for anyone that sees this post here is the full code.


using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using RingCentral;

namespace Recordings
{
    class Program
    {
      


        const string RINGCENTRAL_CLIENTID = "0_mdddddddddQ";
        const string RINGCENTRAL_CLIENTSECRET = "S1h7dddddsWSjUg";
        const bool RINGCENTRAL_PRODUCTION = false;

        const string RINGCENTRAL_USERNAME = "15555555555";
        const string RINGCENTRAL_PASSWORD = "PASSWORD321!";
        const string RINGCENTRAL_EXTENSION = "101";

        static RestClient rcsdk;
        static void Main(string[] args)
        {
            
            rcsdk = new RestClient(RINGCENTRAL_CLIENTID, RINGCENTRAL_CLIENTSECRET, RINGCENTRAL_PRODUCTION);
            rcsdk.Authorize(RINGCENTRAL_USERNAME, RINGCENTRAL_EXTENSION, RINGCENTRAL_PASSWORD).Wait();
            download_call_log_callrecording().Wait();
        }
        static private async Task download_call_log_callrecording()
        {
            var parameters = new ReadCompanyCallLogParameters();
            parameters.dateFrom = "2021-07-05T00:00:00.000Z";
            parameters.view = "Simple";
            parameters.recordingType = "All";

            //Makes the file path
            var path = "c:/recording_content/";
            System.IO.Directory.CreateDirectory(path);
            long timePerApiCall = 6000;

            var resp = await rcsdk.Restapi().Account().CallLog().List(parameters);

            foreach (var record in resp.records)
            {
                Console.WriteLine("-----");
                Console.WriteLine("Call recording: " + record.recording.contentUri);
                var fileName = path + "call_" + record.recording.id + "_recording";
                var start = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
                var result = await rcsdk.Restapi().Account().Recording(record.recording.id).Get();
                if (result.contentType == "audio/mpeg")
                {
                    fileName += ".mp3";
                }
                else // can check if "audio/x-wav"
                {
                    fileName += ".wav";
                }
                var contentUrl = result.contentUri + "?access_token=" + rcsdk.token.access_token;
                WebClient webClient = new WebClient();
                webClient.DownloadFile(contentUrl, fileName);
                Console.WriteLine("Save recording file to the local machine.");
                var end = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
                var delay = (end - start) * 1000;
                Console.WriteLine(delay);
                if (delay < timePerApiCall)
                {
                    Thread.Sleep((int)(timePerApiCall - delay));
                }
            }
        }
    }
}


1 |1500 characters needed characters left characters exceeded

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Jim Lin avatar image
Jim Lin answered Jim Lin commented

so I want to use this in production mode I got the app Active for Production Status. it worked it was downloading all the recordings then it stopped with this error.


System.AggregateException: 'One or more errors occurred. (The remote server returned an error: (429) Too Many Requests.)'

WebException: The remote server returned an error: (429) Too Many Requests.


How can I fix that?


Also, how can I pull the Meta-data from the call log so I have some info on who made the call and the time/day of the call?

3 comments
1 |1500 characters needed characters left characters exceeded

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

You hit the API rate limit 10 downloads per 60 secs. Increase the delay time to 8000 or 10000 and try again.

long timePerApiCall = 8000;
0 Likes 0 ·
Jim Lin avatar image Jim Lin Phong Vu ♦♦ ·

Thank you I change it to 15000 I am in no rush to download all of them.

Thank you for all your help.

there are over 7000 logs to download so this will take some time. Is there a way to download the Meta-data of all the call logs and use the "record.recording.id" as the identifier?

I download the CSV file from the admin portal but it doesn't list the "record.recording.id" so I can't link what mp3 file belongs to the call log CSV.

0 Likes 0 ·
Jim Lin avatar image Jim Lin Phong Vu ♦♦ ·

Got a new error


System.NullReferenceException: 'Object reference not set to an instance of an object.'


RingCentral.CompanyCallLogRecord.recording.get returned null.


I guess one of the logs is null and it doesn't like that.

0 Likes 0 ·
Phong Vu avatar image
Phong Vu answered

How do you handle reading 7000 call logs? Each time you read the call log you will get max 1000 records per page. So with 7000 records, you have yo read 7 times using the next page tokens. You must also take care of the API rate limit (40 calls per 60 secs) of the call log list endpoint. Then when you download the recordings, the rate limit is 10 downloads per 60 secs. Read this article to learn more about API rate limit and how to handle it.

You do need to handle exception and check each call recording record.recording.contentUri is not null.

All in all, my snippet code is a sample and you need to fine tune it for your production code.

1 |1500 characters needed characters left characters exceeded

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Developer sandbox tools

Using the RingCentral Phone for Desktop, you can dial or receive test calls, send and receive test SMS or Fax messages in your sandbox environment.

Download RingCentral Phone for Desktop:

Tip: switch to the "sandbox mode" before logging in the app:

  • On MacOS: press "fn + command + f2" keys
  • On Windows: press "Ctrl + F2" keys