kylegregory / emmasharp Goto Github PK
View Code? Open in Web Editor NEWA .Net wrapper for the Emma API.
License: MIT License
A .Net wrapper for the Emma API.
License: MIT License
Kyle,
Per the following, EMMA is dropping support for TLS 1.0 on 8/1/2017. They recommend implementing TLS 1.2.
https://community.myemma.com/emma/topics/emma-api-no-longer-supporting-tls-1-0-as-of-august-1st-2017
Given that all of my calls to the API are via EMMASharp, I don't believe that I have control over the level of TLS that is used.
Will you be releasing a version of EMMASharp that supports TLS 1.2?
I am current using EMMASharp V1.0.6379.31443.
Hello Kyle,
When I call GetMailingOpens method given MailingID to retrieve opened mail recipients to save mailed sent status to individual records to our system. I've notice that the method "GetMailOpens" only returned list of opened mail members and did not return "Fields" collections. Collection for each opened member with Fields count is 0. for both key and value set... Is it an api bug?
The sample Response output for this method shows fields information in inner array.
We have up to 30 users defined fields in "Fields" collection, and hope we can get at least 3 keys information back in order to verify member in our system since we have shared email address scenarios. Please advise.
Not sure if this is the right place for this but are you guys considering support for .NET Core? I'm getting errors saying something it's not supported on the current platform.
Thanks,
Carson
Recently been receiving the following error: Newtonsoft.Json.JsonSerializationException: Error converting value "q" to type 'System.Nullable`1[EmmaSharp.Models.Members.ImportStatus]'.
After contacting Emma, this was their response:
The 'q' status indicates that the import is queued in the system. This status was recently added to give users more insight into the import's progress. Previously, the API would return the value of 'null' when the import had been added but not processed.
This status should resolve relatively quickly to o
or e
and is more likely to occur when performing multiple imports into the same account.
It would be helpful to update the library to target .net standard 2.0 for improved compatibility. It could also target .Net Framework 4.5 + still I believe. I think the RestSharp dependency may need to be updated to > 1.6, it looks like it supports .net standard and has ways it can work with Json.Net or System.Xml.
Hi Kyle,
Quick questions for you about Emma api :Is there an emma api calls for bulk “archive” subaccount/group/ of members
I only see there are 3 member statuses: “active”, “error”, or “optout” . Is there a way to set a member to “Inactive” ?
Terminology here is very complicated, so let me see if I can help. An 'archived' member is one which has been also been 'deleted.' That endpoint is DeleteMember or to delete multiple use DeleteMembers.
My understanding before GDPR was that 'deleting' via the API or 'archiving' through the front-end just adds a date to the deleted_at parameter. You might find help in the Resource Center especially as it pertains to users signing up again if they are 'archived.' This is probably a bit more complicated/changed after GDPR.
Now as far as making a user account 'inactive' that's an entirely other issue. Sounds like you want to move them into Error, where they won't receive any mailing, but information on that contact can be kept current. Then at some point in the future, you can move them back to Active if needed.
Reach out if you have other questions.
Kyle
Hi Kyle, I tried using your API but I kept getting this error:
Server Error in '/' Application.
JSON integer 3693545479 is too large or small for an Int32. Path 'member_id', line 3, position 26.
Stack Trace:
[JsonReaderException: JSON integer 3693545479 is too large or small for an Int32. Path 'member_id', line 3, position 26.]
Newtonsoft.Json.JsonTextReader.ParseNumber() +1834
Newtonsoft.Json.JsonTextReader.ParseValue() +1164
Newtonsoft.Json.JsonReader.ReadAsInt32Internal() +51
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter) +152
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id) +2194
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) +881
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) +750
Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) +281
Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) +168
Newtonsoft.Json.JsonConvert.DeserializeObject(String value, JsonSerializerSettings settings) +61
EmmaSharp.EmmaApi.Execute(RestRequest request, Int32 start, Int32 end) +590
For now, I'll revert to creating a WebClient and doing the call there, good luck with your API!
Hi Kyle.
I got error JSON integer 18559069618 is too large or small for an Int32. Path '[0].mailing_id', line 1, position 117. on mailing_id when try to call emma api listMailings. Should mailing_id be a long integer instead. Not sure if it was emma bug or just from emmasharp. Can you help?
Thanks
MilSix
Getting an exception when trying to get a count of members in a specific group.
Message = "Unexpected character encountered while parsing value: [. Path '', line 1, position 1."
StackTrace = " at Newtonsoft.Json.JsonTextReader.ReadNumberValue(ReadType readType)\r\n at Newtonsoft.Json.JsonTextReader.ReadAsInt32()\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean ...
TargetSite = {System.Object ReadNumberValue(Newtonsoft.Json.ReadType)}
Hi We have been using EmmaSharp for a few months and suddenly it stopped working on the 8/16 and after further investigating Emma stopped supporting TSL 1.0 on the same day. here is a link to the blog https://community.myemma.com/emma/topics/emma-api-no-longer-supporting-tls-1-0-as-of-august-1st-2017
So my question is this also uses restsharp as well so one of these library's needs to be updated.
http://api.myemma.com/api/external/subscriptions.html
Will subscriptions be available in a coming EmmaSharp release?
According to the API /#account_id/mailings/#mailing_id, the data returned has an array object "recipient_groups". It has two properties "member_group_id" and "name" . When GetMailing(memberId) is used, only MemberId is binded and not rest of the Group properties.
Method: GetMailingCustomerShares
API Endpoint: GET /#account_id/response/#mailing_id/customer_shares
Method: GetMailingCustomerShareClicks
API Endpoint: GET /#account_id/response/#mailing_id/customer_share_clicks
Method: GetMailingCustomerShare
API Endpoint: GET /#account_id/response/#share_id/customer_share
Return values for the above endpoints are unknown, and may be depreciated.
Method: GetMemberOptout
API Endpoint: GET /#account_id/members/#member_id/optout
Expected: Provided member is opted out, information is returned (docs describe date and mailing_id)
Happens: Returns empty array.
Method: CreateSavedSearch
API Endpoint: POST /#account_id/searches
Method: UpdateSavedSearch
API Endpoint: PUT /#account_id/searches/#search_id
Creating and Updating searches trips up Emma's API. The API returns the following error: {"error": "'unicode' object has no attribute 'pop'"}
It is throwing a BadRequest for this call due to the request parameter groupId is passed as a Get/post Parameter. but this is a put request should be in body. below is my fix.
public bool UpdateStatusOfGroupMembersBasedOnCurrentStatus(MemberStatusShort statusFrom, MemberStatusShort statusTo, string groupId = "")
{
var request = new RestRequest(Method.PUT);
request.Resource = "/{accountId}/members/status/{statusFrom}/to/{statusTo}";
request.AddUrlSegment("statusFrom", statusFrom.ToEnumString());
request.AddUrlSegment("statusTo", statusTo.ToEnumString());
if (!string.IsNullOrWhiteSpace(groupId))
request.AddJsonBody(new { group_id = groupId});
return Execute<bool>(request);
}
Thanks.
Kyle,
Does the Emma API have a method for retrieving members by field value? The only way that I've figured out how to do this is to get all members and then filter the list by the desired field's value. Unfortunately, with 12.5K members, that's 25 GETS @ 500 per. The resulting response time can be measured in minutes.
Thx.
Hi!
I'm just wondering if it is possible to make this wrapper work if using .NET 6 (or other Core versions).
Thanks!
~Samantha
I am trying to execute an "AddNewMembers" command but am receiving the following error. This is my third installation but it is the only one that is failing.
This code runs successfully with two other EMMA clients. I have included the "GetImportInformation" of a successful run at the end of this note.
Please note that this is of extremely high priority as my client needs to send their EMMA campaign this week (i.e., 4/2/2018).
Thank you.
ERROR MESSAGE:
JSON integer 4939648906 is too large or small for an Int32. Path 'import_id', line 2, position 25.
SOFTWARE VERSIONS:
EmmaSharp: 1.1.0
Newtonsoft.Json: 11.0.2
CODE:
//--------------------------------------
// Create EMMA Members and add to Group
//--------------------------------------
List Members = new List();
foreach (var contact in contactsWithEmails)
{
MemberBulk memberBulk = new MemberBulk();
memberBulk.MemberEmail = contact.Email;
//V1.1.0
Dictionary<string, object> fields = new Dictionary<string, object>();
fields.Add(settings.FirstNameFieldName, contact.FirstName);
fields.Add(settings.LastNameFieldName, contact.LastName);
fields.Add(settings.InterActionIdFieldName, contact.Id.ToString());
memberBulk.Fields = fields;
Members.Add(memberBulk);
}
AddMembers addMembers = new AddMembers();
addMembers.AddOnly = false;
addMembers.Members = Members;
addMembers.GroupIds = new List() { Convert.ToInt32(focGroupID) };
MembersAdd membersAdd = emmasharp.AddNewMembers(addMembers);
OBJECT VALUES:
Members Count = 3 System.Collections.Generic.List<EmmaSharp.Models.MemberBulk>
[0] {EmmaSharp.Models.MemberBulk} EmmaSharp.Models.MemberBulk
Fields Count = 3 System.Collections.Generic.Dictionary<string, object>
[0] {[first_name, Steve]} System.Collections.Generic.KeyValuePair<string, object>
[1] {[last_name, xxx]} System.Collections.Generic.KeyValuePair<string, object>
[2] {[InterActionID, F2i2]} System.Collections.Generic.KeyValuePair<string, object>
[1] {EmmaSharp.Models.MemberBulk} EmmaSharp.Models.MemberBulk
Fields Count = 3 System.Collections.Generic.Dictionary<string, object>
[0] {[first_name, Shelley]} System.Collections.Generic.KeyValuePair<string, object>
[1] {[last_name, xxx]} System.Collections.Generic.KeyValuePair<string, object>
[2] {[InterActionID, F2i129]} System.Collections.Generic.KeyValuePair<string, object>
[2] {EmmaSharp.Models.MemberBulk} EmmaSharp.Models.MemberBulk
Fields Count = 3 System.Collections.Generic.Dictionary<string, object>
[0] {[first_name, Colleen]} System.Collections.Generic.KeyValuePair<string, object>
[1] {[last_name, xxx]} System.Collections.Generic.KeyValuePair<string, object>
[2] {[InterActionID, F2i202]} System.Collections.Generic.KeyValuePair<string, object>
focGroupID "4031370" string
STACK TRACE:
at Newtonsoft.Json.JsonTextReader.ParseReadNumber(ReadType readType, Char firstChar, Int32 initialPosition)
at Newtonsoft.Json.JsonTextReader.ParseNumber(ReadType readType)
at Newtonsoft.Json.JsonTextReader.ReadNumberValue(ReadType readType)
at Newtonsoft.Json.JsonTextReader.ReadAsInt32()
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value)
at EmmaSharp.EmmaApi.Execute[T](RestRequest request, Int32 start, Int32 end)
at EmmaSharp.EmmaApi.AddNewMembers(AddMembers members)
at INTCS.InterAction.DotNET.Api.INTCSContacts.sendContactsToEMMA(Folder folder, Contact folderOnlyContact, String groupIdMailingId, AppSettings settings, EmmaApi emmasharp, MailingType mailType) in C:\LNGOAKTFSA001\EKH_S\INTCS.EMMA.Integration\INTCS.InterAction.DotNET.Api\INTCSContacts.cs:line 767
SUCCESSFUL EXECUTION WITH A DIFFERENT EMMA CLIENT:
[GetImportInformation}
Results of ImportId: 42572062
{
"import_id": 42572062,
"status": "o",
"style": "add_and_update",
"import_started": "@d:2018-04-02T20:29:38",
"account_id": 1796382,
"error_message": null,
"num_members_updated": 0,
"source_filename": null,
"fields_updated": [
{
"shortcut_name": "first_name",
"account_id": null,
"required": false,
"field_id": 766238,
"short_display_name": null,
"deleted_at": null,
"options": null,
"display_name": "First name",
"field_type": "text",
"widget_type": "text",
"column_order": null
},
{
"shortcut_name": "last_name",
"account_id": null,
"required": false,
"field_id": 767262,
"short_display_name": null,
"deleted_at": null,
"options": null,
"display_name": "Last name",
"field_type": "text",
"widget_type": "text",
"column_order": null
},
{
"shortcut_name": "interactionid",
"account_id": null,
"required": false,
"field_id": 792862,
"short_display_name": null,
"deleted_at": null,
"options": null,
"display_name": "InterActionID",
"field_type": "text",
"widget_type": "text",
"column_order": null
}
],
"num_members_added": 1,
"import_finished": "@d:2018-04-02T20:29:39",
"groups_updated": [
{
"active_count": null,
"deleted_at": null,
"error_count": null,
"optout_count": null,
"group_type": "g",
"member_group_id": 5661982,
"purged_at": null,
"account_id": null,
"group_name": "Wiss Emma Integration Demo (2018-04-02 20:27)"
}
],
"num_skipped": 0,
"num_duplicates": 0
}
RemoveMembersFromGroup needs to return List instead of List in EmmaSharp\Groups.cs
public List<int> RemoveMembersFromGroup(string memberGroupId, MemberIdList memberIds)
should be..
public List<long> RemoveMembersFromGroup(string memberGroupId, MemberIdList memberIds)
Below is stack trace from a live call that is failing for me.
Newtonsoft.Json.JsonReaderException: JSON integer 2169469051 is too large or small for an Int32. Path '[0]', line 2, position 12.
at Newtonsoft.Json.JsonTextReader.ParseReadNumber(ReadType readType, Char firstChar, Int32 initialPosition)
at Newtonsoft.Json.JsonTextReader.ReadNumberValue(ReadType readType)
at Newtonsoft.Json.JsonTextReader.ReadAsInt32()
at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at EmmaSharp.EmmaApi.Execute[T](RestRequest request, Int32 start, Int32 end)
at EmmaSharp.EmmaApi.RemoveMembersFromGroup(String memberGroupId, MemberIdList memberIds)
Method: VaildatePersonalizationSyntax
API Endpoint: POST /#account_id/mailings/validate
If anything is included in html_body or plaintext params, the API returns: "HTML content is missing a required tag: Optout Link Tag". When either [% unsubscribe %] or [% unsub_url %] is added, the API returns: "Unknown tag name unsubscribe
" or "Unknown tag name unsub_url
". Obviously, the API should be able to validate with one of the two included. Also tested with [% management %] which is included in template builder with no luck.
Hi, it might not be new issue but I just wonder if you guys experience the same error below when call Searches methods?
I created 5 segments and try to call GetSearchesCount to see if it return my count of 5 segments. But I kept getting the error 'Could not cast or convert from System.Int64 to System.Collections.Generic.List`1....
Detail: Error converting value 1 to type 'System.Collections.Generic.List`1[EmmaSharp.Models.Searches.Search]. at
Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable(Object value, Type
initialType, Type targetType) at
Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(Object initialValue,
CultureInfo culture, Type targetType) at
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader
reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
Hi, I can't seem to figure out how to update a custom field which is a multi select checkbox field. Would you know how to do this?
This is what I have at the moment. Note, that the "interests" column is a custom multi checkbox column
var memberBulk = emails.Select(e => new EmmaSharp.Models.MemberBulk
{
MemberEmail = e,
Fields = new Dictionary<string, string> {
{ "interests", "{advocacy}" },
{ "newcustomfield", "TestUpdate5" }
}
}).ToList();
var members = new EmmaSharp.Models.AddMembers()
{
AddOnly = false
};
members.Members.AddRange(memberBulk);
Any help would be appreciated.
Thanks.
Hey bro... thanks so much for this library... saved me a ton of time!
Real quick, the NuGet package is out-of-date because it doesn't include the changes you made to Int32 on the member object to handle large ids. I ended up just dl'ing the library and compiling it locally, but thought you'd want to know in case you didn't. Again, thanks, man!
Method: GetMembersAffectedByImport
API Endpoint: GET /#account_id/members/imports/#import_id/members
Change Type. Documented there should only be two: a ('add') and u ('add and update'). However test cases have run into an 'n' status, of which the meaning is unknown.
Method: GetResponseSummary
API Endpoint: GET /#account_id/response?range=2015-12-08
Expected: If range is a single date, API will return summary of mailings sent on that date.
Happens: No mailings/summary is returned.
Note: Other range values with '~' return mailings as expected. This happens with or without the archived parameter.
I am looking for an option to use this wrapper on one of my big projects that includes email management. I am confused with this method AddMemberToGroups which expects memberid and List as members. I am passing the same thing but getting error saying unable to parse json request, which I am assuming is because of integers. Can you help verify if this method is working?
P.S. I just started today, so I might be confused too.
This is how I am using it.
public bool AddContactToMailingList(string emailAddress, List<int> groupIds)
{
try
{
var emmaApi = EmmaApiConfig();
var memberInfo = emmaApi.GetMemberByEmail(emailAddress);
string memberId = memberInfo.MemberId.ToString();
EmmaSharp.Models.Members.MemberGroups groups = new EmmaSharp.Models.Members.MemberGroups
{
GroupIds = groupIds
};
emmaApi.AddMemberToGroups(memberId, groups);
return true;
}
catch (Exception ex)
{
var exception = ex;
return false;
}
}
It seems development on RestSharp is slowing because of HttpClient
, which is in .NET 4.5 and .NET Core. Restsharp improved getting the project up and running quickly, but may be more overhead than is needed.
Issue open for comments.
I really planned to use this wrapper because it would be convenient / handy. However:
• ran a simple test – console app , but I consistently fail with a 401 : "Unexpected response status 401 with body: (empty)" | "StatusCode: Unauthorized, Content-Type: text/plain, Content-Length: 0)"
• however, plain requests when submitted in a browser succeed and return proper results
Can anyone help what am I missing here ? A config parameter maybe ?
First of all, I'm new to REST programming, and I would like to thank you very much for this wonderful API wrapper. Without this, I'd probably be pulling out my teeth to get Emma API to work.
Anyway, I was trying to get the GetMemberGroups method to work, to test if I could get a simple GET request. To make the long story short, I had to change the return value from MemberGroups
to List<Groups>
to get it to work, because Emma returns an array, not an object. Otherwise, JsonConvert.DeserializeObject
will throw an exception. So Emma returns something like this:
[
{
\"active_count\": 2,
\"deleted_at\": null,
\"error_count\": 0,
\"optout_count\": 0,
\"group_type\": \"g\",
\"member_group_id\": xxxxxxx,
\"purged_at\": null,
\"account_id\": xxxxxxx,
\"group_name\": \"Microsite Marketing Form\"
}
]
So, the method works now (BTW, I'm not forking the project, I just downloaded the zip and doing this on my own).
The next thing I have to tackle is the AddOrUpdateSingleMember
method, which I believe corresponds to
POST /#account_id/members/add
API in the Emma docs. When I called AddOrUpdateSingleMember
, it throws a Unable to parse JSON request
exception. I believe that the data that I'm trying to pass (i.e., MemberGroups
, List<Field>
) are in the wrong format?
Because I need a method to add a member to Emma (we have a web page that simply prompts user to enter her name and email address, and then I send these data to Emma), and because I can't figure out the problem with AddOrUpdateSingleMember
, I probably just need to pass a raw Json data to Emma (I was able to create a member passing a raw Json object using Google Chrome's Advanced REST Client).
Any thoughts on this problem? Has Emma changed something that the API wrapper was not able to get updated with?
Thanks,
Al
I am adding emails to MEMBERS and GROUPS. Once added, I perform a LISTGROUPMEMBERS as I need to retrieve the MEMBERIDs that have been created. However, there appears to be a time lag between the "ADD" and what gets listed.
In the example below, I am adding a fifth email to the group. While the EMMA UI correctly displays the 5 members, the LISTGROUPMEMBERS only retrieves 4. If I wait a while and then reissue the LISTGROUPMEMBERS, I get all 5.
What am I doing wrong.?
Calling any method is giving me the following error:
'Self referencing loop detected for property 'ManifestModule' with type 'System.Reflection.RuntimeModule'. Path 'OnBeforeDeserialization.Method.Module.Assembly'.'
This appears to be related to serializing/tracing each request. (Line 68, EmmaSharp.cs):
Trace.WriteLine(request.JsonSerializer.Serialize(request));
Removing the trace allows method calls to work again. This seems like debug type functionality, I wonder if a flag could be added to disable it, or it could be disabled altogether? Happy to open a PR to address it.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.