UGC(User Generated Contents) のモデレートまわりについて解説をしていきます。
ユーザーが自由にコンテンツを生成できるということは、内容に問題があるコンテンツも公開されてしまうリスクがあるということです。
たとえばウイルスに感染しているファイルであったり、スパム的な内容などですね。
問題のあるコンテンツを放置しておくと他のユーザーにも影響がでたり、せっかく遊んでくれているユーザーが離れてしまうかもしれません。
そこで PlayFab では、ユーザーが問題がありそうなコンテンツを発見した場合に、報告してもらう仕組みが用意されています。
本記事では、ユーザーから報告してもらう機能と、管理者がモデレート(承認したり、否認したりすること)をする機能について解説しました。
最後まで読むことで、実装イメージが湧いてくると思います。
それではさっそく見ていきましょう。
アイテムの報告
問題がありそうなアイテムをユーザーから報告してもらうには、ReportItem を使用します。
private void ReportItem()
{
PlayFabEconomyAPI.ReportItem(new ReportItemRequest
{
Id = "f464bc98-09ae-4bc8-a218-3a6100b6755e",
Reason = "誤解を招く内容です。",
ConcernCategory = ConcernCategory.MisleadingApp
}, result =>
{
Debug.Log("アイテムの報告成功");
}, error =>
{
Debug.Log(error.GenerateErrorReport());
});
}
Id には、アイテム ID を指定します。今回は固定値としましたが、本来であればアイテムの一覧を取得して、そこから個別の ID を取得するのが望ましいです。
Reason に理由を入力し、任意で ConcernCategory に報告内容のカテゴリを指定します。
ConcernCategory の種類は以下のとおりです。
列挙体 | 意味 |
---|---|
None | なし(初期値) |
OffensiveContent | 攻撃的な内容 |
ChildExploitation | 児童虐待に関する内容 |
MalwareOrVirus | マルウェアやウイルス |
PrivacyConcerns | プライバシーの侵害 |
MisleadingApp | 誤解を招く内容 |
PoorPerformance | パフォーマンスの低下 |
ReviewResponse | レビューの回答 |
SpamAdvertising | スパム広告 |
Profanity | 冒とく的な内容 |
アイテムの報告がされたかどうかは、データエクスプローラーから確認ができます。
アイテムの報告をしても何か通知がくるわけではないので、定期的に確認したほうがいいでしょう。
クエリで「item_reported」を指定して実行をすると、ReportItem を実行したログのみが抽出されます。
このログの内容をもとに、報告されたアイテムをどう扱うかについて検討することになります。
{
"SchemaVersion": "2.0.1",
"FullName": {
"Namespace": "playfab.catalog",
"Name": "item_reported"
},
"Id": "02d12302b1ab4902ad88f2465a4d8d62",
"Timestamp": "2021-12-06T12:38:58.0076469Z",
"Entity": {
"Id": "9CED3069C7EC37C0",
"Type": "title_player_account"
},
"Originator": {
"Id": "9CED3069C7EC37C0",
"Type": "title_player_account"
},
"OriginInfo": null,
"Payload": {
"ItemId": "f464bc98-09ae-4bc8-a218-3a6100b6755e",
"ItemType": "ugc",
"CreatorEntityKey": {
"Id": "9CED3069C7EC37C0",
"Type": "title_player_account"
},
"ConcernCategory": "MisleadingApp",
"Reason": "誤解を招く内容です。"
},
"EntityLineage": {
"namespace": "XXXXXXXXXXXXXXX",
"title": "XXXXX",
"master_player_account": "2B416FE1BB5AEE86",
"title_player_account": "9CED3069C7EC37C0"
},
"ExperimentVariants": null
}
モデレーション状態の変更
モデレーション状態の変更は、SetItemModerationState を使用します。
ただし、この API はタイトルエンティティの権限が必要なため、クライアント側から処理することはできません。
ちなみにクライアントから実行してみると「タイトル権限が必要です」とエラーが出て、やはり実行できません。
ということで、Azure Fucntions と連携したサーバー処理で書いていきましょう。
以下がサーバー側の処理になります。
[FunctionName("SetItemModerationState")]
public static async Task<dynamic> SetItemModerationState(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
string body = await req.ReadAsStringAsync();
var context = JsonConvert.DeserializeObject<FunctionExecutionContext<dynamic>>(body);
var args = context.FunctionArgument;
string playFabId = context.CallerEntityProfile.Lineage.MasterPlayerAccountId;
var authApi = GetAuthInstance(context.TitleAuthenticationContext);
var auth = await authApi.GetEntityTokenAsync(new PlayFab.AuthenticationModels.GetEntityTokenRequest
{
Entity = new PlayFab.AuthenticationModels.EntityKey
{
Id = context.TitleAuthenticationContext.Id,
Type = "title"
}
});
var economyApi = GetEconomyInstance(context.TitleAuthenticationContext, authApi.authenticationContext);
var result = await economyApi.SetItemModerationStateAsync(new PlayFab.EconomyModels.SetItemModerationStateRequest
{
Id = args["ItemId"],
Status = ModerationStatus.Approved,
Reason = "特に問題なかったため。"
});
return new
{
error = PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer).SerializeObject(result.Error),
result = PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer).SerializeObject(result.Result)
};
}
private static PlayFabEconomyInstanceAPI GetEconomyInstance(TitleAuthenticationContext titleAuthenticationContext, PlayFabAuthenticationContext authContext)
{
var apiSettings = new PlayFabApiSettings
{
TitleId = titleAuthenticationContext.Id,
DeveloperSecretKey = Environment.GetEnvironmentVariable("PLAYFAB_DEV_SECRET_KEY", EnvironmentVariableTarget.Process),
};
return new PlayFabEconomyInstanceAPI(apiSettings, authContext);
}
private static PlayFabAuthenticationInstanceAPI GetAuthInstance(TitleAuthenticationContext titleAuthenticationContext)
{
var apiSettings = new PlayFabApiSettings
{
TitleId = titleAuthenticationContext.Id,
DeveloperSecretKey = Environment.GetEnvironmentVariable("PLAYFAB_DEV_SECRET_KEY", EnvironmentVariableTarget.Process),
};
return new PlayFabAuthenticationInstanceAPI(apiSettings);
}
全体の流れとしては以下のとおりです。
- GetEntityTokenAsync でタイトルの実行権限に昇格する
- エコノミー API を実行するためのインスタンスを生成する
- SetItemModerationState を実行する
サーバー処理としたからといって、タイトル権限の実行にならない点にも注意が必要です。
実行するときの ModerationStatus の種類は以下のとおりです。
列挙体 | 意味 |
---|---|
AwaitingModeration | モデレートを待機しています |
Rejected | 拒否されました |
Approved | 承認済み |
Unknown | 不明 |
AwaitingModeration は、ReportItem で報告されたアイテムをモデレーション中に変更するときに使用するといいでしょう。
Approved は内容として特に問題がなかったときに設定し、逆に問題があった場合は Rejected を設定します。
Unknown は特に使用しなくていいと思います。
Approved 以外のステータスでは、他のプレイヤーからは見えなくなります。
次にクライアント側からの呼び出し処理についてです。
private void SetItemModerationStateResponse()
{
CallAzureFunctions<SetItemModerationStateResponse>(
"SetItemModerationState",
new { ItemId = "2e9a6eb9-fe21-4597-b802-6f0e10290d50" },
funcResult =>
{
Debug.Log("アイテムのモデレーション状態の更新成功");
}, funcError =>
{
Debug.Log("実行時にエラーが発生しました。");
Debug.Log(funcError.GenerateErrorReport());
});
}
/// <summary>
/// Azure Functions の呼び出し
/// </summary>
/// <typeparam name="T">取得結果の型</typeparam>
/// <param name="funcName">呼び出す関数名</param>
/// <param name="funcParam">サーバーに渡すパラメータ</param>
/// <param name="onSuccess">成功時コールバック</param>
/// <param name="onError">失敗時コールバック</param>
public void CallAzureFunctions<T>(
string funcName,
object funcParam,
Action<T> onSuccess = null,
Action<PlayFabError> onError = null)
{
PlayFabCloudScriptAPI.ExecuteFunction(new ExecuteFunctionRequest()
{
Entity = new PlayFab.CloudScriptModels.EntityKey()
{
Id = PlayFabSettings.staticPlayer.EntityId,
Type = PlayFabSettings.staticPlayer.EntityType
},
FunctionName = funcName,
FunctionParameter = funcParam,
GeneratePlayStreamEvent = true
}, result =>
{
Debug.Log("Azure Functions 実行成功! ");
// 返り値の取り出し
var jsonResult = (JsonObject)result.FunctionResult;
var funcSuccess = PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer).DeserializeObject<T>(jsonResult["result"].ToString());
var funcError = PluginManager.GetPlugin<ISerializerPlugin>(PluginContract.PlayFab_Serializer).DeserializeObject<PlayFabError>(jsonResult["error"].ToString());
if (result.FunctionResultTooLarge != null && (bool)result.FunctionResultTooLarge)
{
onError?.Invoke(funcError);
return;
}
if (funcError == null)
onSuccess?.Invoke(funcSuccess);
else
onError?.Invoke(funcError);
}, error =>
{
onError.Invoke(error);
});
}
引数としてモデレーション更新対象の ItemId を渡して SetItemModerationState を呼び出しているだけですね。
実行して、以下のようにログが出れば実行成功です。
更新したアイテムのモデレート状態を見てみると、指定したとおり「承認済み」として更新されていることが確認できました。
最後に
ユーザーから報告してもらう機能と、管理者がモデレートする機能について解説しました。
やはり運営していく上でも、問題のあるコンテンツを放置しておくとユーザーが離脱することにもつながります。
本記事の内容をもとに、コンテンツを管理する機能も取り入れてみましょう。
PlayFab のことをもっと皆さんに知ってもらいたくて、合計500ページ以上にもなる書籍を5冊に分けて執筆しました。
私の知識をすべて詰め込んでいるので、ゲーム開発をさらに加速させたい方はぜひご覧ください。