G-gen ã®é«å®®ã§ãã Google Calendar API ãš Google Meet REST API ãçµã¿åãããå®è£
æã«ãããã€ãã®ãšã©ãŒãçºçããŸããããã®å
容ãšåå ã®èå¯ã玹ä»ããŸãã ã¯ããã« äŒè°ã¹ããŒã¹äœææã«ãšã©ãŒ å®è£
ãšã©ãŒ åå åé¿ç Meet äŒè°ã®èšå®ç·šéæã«ãšã©ãŒ å®è£
ãšã©ãŒ åå åé¿ç ã¯ãã㫠以åã®èšäºãAPIçµç±ã§Google Meetã®ææç©äœæãæå¹åããã«ã¬ã³ããŒäºå®ãäœæãããã§ã¯ãGoogle Calendar API ãš Google Meet REST API ãå©çšããGoogle Meet ã®ææç©äœæãæå¹åããã«ã¬ã³ããŒã®äºå®ãäœæããæ¹æ³ã玹ä»ããŸããã blog.g-gen.co.jp Google Meet ã®ææç©ïŒã¢ãŒãã£ãã¡ã¯ãïŒäœæãæå¹åããã«ã¬ã³ããŒäºå®ã API ã§äœæããããã«ã以äžã®3ã€ã®ã¢ãããŒããæ€èšŒããŸããã Calendar API ã§ã€ãã³ããäœæãããæ¬¡ã«ãMeet REST APIïŒ google-apps-meet ã©ã€ãã©ãªçµç±ïŒã§ã¢ãŒãã£ãã¡ã¯ãçæãæå¹åããã¹ããŒã¹ãäœæãããæ¬¡ã«ãã€ãã³ããšã¹ããŒã¹ãçŽã¥ããã Calendar API ã§ã€ãã³ããšã¹ããŒã¹ãäœæãããæ¬¡ã«ãMeet REST APIïŒ google-api-python-client ã©ã€ãã©ãªçµç±ïŒã§ã¹ããŒã¹ã®ã¢ãŒãã£ãã¡ã¯ãçæãæå¹åããã Calendar API ã§ã€ãã³ããäœæãããæ¬¡ã«ãMeet REST APIïŒ google-api-python-client ã©ã€ãã©ãªçµç±ïŒã§ã¢ãŒãã£ãã¡ã¯ãçæãæå¹åããã¹ããŒã¹ãäœæãããæ¬¡ã«ãã€ãã³ããšã¹ããŒã¹ãçŽã¥ããã ããããã®ã¢ãããŒãã®å®è£
æ¹æ³ã®éããšåäœçµæã¯ã以äžã®è¡šã®éãã§ãã No. 䜿çšã©ã€ãã©ãªïŒMeetïŒ ã¹ããŒã¹ã®äœæ ã¢ãŒãã£ãã¡ã¯ãèšå® ã€ãã³ããšã®çŽã¥ã åäœçµæ 1 google-apps-meet ã©ã€ãã©ãª Meet REST API ã¹ããŒã¹äœææã«æå® Calendar API ã§äœæããã€ãã³ããæŽæ°ããçŽã¥ã äŒè°ã¹ããŒã¹äœææã«ãšã©ãŒ 2 google-api-python-client ã©ã€ãã©ãª Calendar API Meet REST API ã§äœæãããã¹ããŒã¹ã®èšå®ãæŽæ° Calendar API ã§ã€ãã³ãäœææã«èªåã§çŽã¥ã Meet äŒè°ã®èšå®ç·šéæã«ãšã©ãŒ 3 google-api-python-client ã©ã€ãã©ãª Meet REST API ã¹ããŒã¹äœææã«æå® Calendar API ã§äœæããã€ãã³ããæŽæ°ããçŽã¥ã æåŸ
ããåäœïŒä»¥åã®èšäºïŒ åœèšäºã§ã¯ãäžèšã®ãã¡ãšã©ãŒãçºçãã ã±ãŒã¹1 ãš ã±ãŒã¹2 ã«ã€ããŠããšã©ãŒã®è©³çްãåå ãããã³åé¿çã解説ããŸãã äŒè°ã¹ããŒã¹äœææã«ãšã©ãŒ å®è£
以äžã®ã³ãã³ããå®è¡ããŠã google-apps-meet ã©ã€ãã©ãªã远å ã§ã€ã³ã¹ããŒã«ããŸãã uv add google-apps-meet == 0 . 2 . 0 2025幎11ææç¹ã§ããŒã¿çã§å
¬éãããŠããæ©èœã䜿çšããŠã main.py ã«ä»¥äžã®ã³ãŒããå®è£
ããŸãã import os import datetime from google.auth.transport import requests from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from google.apps.meet_v2beta import SpacesServiceClient from google.apps.meet_v2beta.types import Space, SpaceConfig, CreateSpaceRequest from googleapiclient.discovery import build def authorize () -> Credentials: """Calendar API ãš Meet API ãåŒã³åºãããã« OAuth 2.0 èªèšŒãè¡ããCredentials ãªããžã§ã¯ããè¿ã""" CLIENT_SECRET_FILE = "./client_secret.json" credentials = None if os.path.exists( "token.json" ): credentials = Credentials.from_authorized_user_file( "token.json" ) if credentials is None : flow = InstalledAppFlow.from_client_secrets_file( CLIENT_SECRET_FILE, scopes=[ "https://www.googleapis.com/auth/calendar.events.owned" , "https://www.googleapis.com/auth/meetings.space.created" , ], ) flow.run_local_server(port= 0 ) credentials = flow.credentials if credentials and credentials.expired: credentials.refresh(requests.Request()) if credentials is not None : with open ( "token.json" , "w" ) as f: f.write(credentials.to_json()) return credentials USER_CREDENTIALS = authorize() def create_event (): """Calendar API ã䜿çšããŠã€ãã³ããäœæãã""" # ãµãŒãã¹ãªããžã§ã¯ãã®æ§ç¯ service = build( "calendar" , "v3" , credentials=USER_CREDENTIALS) # æå®ããæå»ãã1æéã®äºå®ãäœæ now = datetime.datetime.now() startTime = now.isoformat() endTime = (now + datetime.timedelta(hours= 1 )).isoformat() event = { "summary" : "Google Calendar Meet Test Event" , "start" : { "dateTime" : startTime, "timeZone" : "Asia/Tokyo" , }, "end" : { "dateTime" : endTime, "timeZone" : "Asia/Tokyo" , }, } # èªèº«ã®ã«ã¬ã³ããŒã«äºå®ã远å event = service.events().insert(calendarId= "primary" , body=event).execute() return event def create_space () -> Space: """Meet API ã䜿çšã㊠ã¢ãŒãã£ãã¡ã¯ãçæãæå¹åãã Space ãªãœãŒã¹ãäœæãã""" # ã¯ã©ã€ã¢ã³ããäœæãã Space ãäœæ client = SpacesServiceClient(credentials=USER_CREDENTIALS) request = CreateSpaceRequest() space = client.create_space(request=request) # artifact ã®èªåçæãæå¹å space.config = SpaceConfig( artifactConfig=SpaceConfig.ArtifactConfig( recordingConfig=SpaceConfig.ArtifactConfig.RecordingConfig( autoRecordingGeneration=SpaceConfig.ArtifactConfig.RecordingConfig.AutoRecordingGeneration.ON ), transcriptionConfig=SpaceConfig.ArtifactConfig.TranscriptionConfig( autoTranscriptionGeneration=SpaceConfig.ArtifactConfig.TranscriptionConfig.AutoTranscriptionGeneration.ON, ), smartNotesConfig=SpaceConfig.ArtifactConfig.SmartNotesConfig( autoSmartNotesGeneration=SpaceConfig.ArtifactConfig.SmartNotesConfig.AutoSmartNotesGeneration.ON, ), ), ) return space def update_event (eventId: str = None , meetUri: str = None ): """Calendar API ã䜿çšããŠæ¢åã®ã€ãã³ãã« Meet æ
å ±ã远å ãã""" # ãµãŒãã¹ãªããžã§ã¯ãã®æ§ç¯ service = build( "calendar" , "v3" , credentials=USER_CREDENTIALS) # äºå®ã®æŽæ° event = service.events().get(calendarId= "primary" , eventId=eventId).execute() event[ "conferenceData" ] = { "conferenceSolution" : { "key" : { "type" : "hangoutsMeet" }, }, "entryPoints" : [ { "entryPointType" : "video" , "uri" : meetUri, } ], } updated_event = ( service.events() .update( calendarId= "primary" , eventId=event[ "id" ], body=event, conferenceDataVersion= 1 , ) .execute() ) return updated_event def main (): """ã¡ã€ã³åŠç""" # ã€ãã³ããš Space ã®äœæ event = create_event() print (f "Google Calendar URL {event.get('htmlLink')}" ) space = create_space() if __name__ == "__main__" : main() 以äžã®é¢æ°ã¯ä»¥åã®èšäºãšåæ§ã§ãã authorize create_event update_event 以åã®èšäºãšç°ãªãã®ã¯ã create_space 颿°ã«ããã以äžã®åŠçã®å®è£
ã§ãã èªèšŒæ
å ±ã䜿çšããŠã SpacesServiceClient ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã CreateSpaceRequest ã¯ã©ã¹ã®ã€ã³ã¹ã¿ã³ã¹ãåŒæ°ã«èšå®ãã create_space ã¡ãœãããåŒã³åºãã¹ããŒã¹ãäœæãããªã¯ãšã¹ããéä¿¡ããŸãã åŠç 2. ã§äœæããã¹ããŒã¹ã«å¯ŸããŠã以äžã®ã¯ã©ã¹ã䜿çšã㊠Google Meet ã®ææç©äœæãæå¹åããŸãã ã¯ã©ã¹å 説æ SpaceConfig äŒè°ã¹ããŒã¹å
šè¬ã®æ§æ ArtifactConfig äŒè°ã§ãµããŒããããŠããèªåçæã¢ãŒãã£ãã¡ã¯ãã«é¢ããæ§æ RecordingConfig é²ç»ã®æ§æ TranscriptionConfig èªåæåèµ·ããã®æ§æ SmartNotesConfig èªåã¹ããŒãã¡ã¢ã®æ§æ åè : REST Resource: spaces ãšã©ãŒ åŠç 2. ã®å®è¡æïŒ80è¡ç®ïŒã«ã以äžã®äŸå€ãçºçããŸãã äŸå€ãçºçããŸãã: MethodNotImplemented 501 Method not found. grpc. channel. InactiveRpcError: <_InactiveRpcError of RPC that terminated with: status = StatusCode.UNIMPLEMENTED details = "Method not found." debug_error_string = "UNKNOWN:Error received from peer ipv4:{IPã¢ãã¬ã¹}:443 {grpc_message:"Method not found.", grpc_status:12}" åå ãšã©ãŒã¡ãã»ãŒãžã«ãgRPC ã®æšæºçãªã¹ããŒã¿ã¹ã³ãŒãã§ãã StatusCode.UNIMPLEMENTED ïŒãšã©ãŒã³ãŒã 12ïŒãè¿åŽãããŠãããããªã¯ãšã¹ããããæäœãå®è£
ãããŠããªãããŸã㯠API ã«ãã£ãŠãµããŒããããŠããªããããšãããããŸãã ããŒã¿çã®æ©èœã§ããããããµãŒããŒåŽã§åœè©²ã¡ãœããããŸã å®è£
ãããŠããªãããããã¯å
¬éãããŠããªãããšãåå ãšæšæž¬ãããæ¬çªç°å¢ã§ã®äœ¿çšã«ã¯ååãªæ³šæãå¿
èŠã§ãã åé¿ç åŠç 2. ã google-api-python-client ã©ã€ãã©ãªçµç±ã§ Meet REST API ãåŒã³åºãå®è£
ã«å€æŽããããšã§æåŸ
ããåäœã«ãªããŸãã Meet äŒè°ã®èšå®ç·šéæã«ãšã©ãŒ å®è£
main.py ã«ä»¥äžã®ã³ãŒããå®è£
ããŸããæ¬å®è£
ã§ã¯ãAPI ã®åŒã³åºãã¯ã google-api-python-client ã©ã€ãã©ãªã䜿çšããŠè¡ã£ãŠããŸãã import os import datetime import uuid from google.auth.transport import requests from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build def authorize () -> Credentials: """Calendar API ãš Meet API ãåŒã³åºãããã« OAuth 2.0 èªèšŒãè¡ããCredentials ãªããžã§ã¯ããè¿ã""" CLIENT_SECRET_FILE = "./client_secret.json" credentials = None if os.path.exists( "token.json" ): credentials = Credentials.from_authorized_user_file( "token.json" ) if credentials is None : flow = InstalledAppFlow.from_client_secrets_file( CLIENT_SECRET_FILE, scopes=[ "https://www.googleapis.com/auth/calendar.events.owned" , "https://www.googleapis.com/auth/meetings.space.created" , "https://www.googleapis.com/auth/meetings.space.settings" , ], ) flow.run_local_server(port= 0 ) credentials = flow.credentials if credentials and credentials.expired: credentials.refresh(requests.Request()) if credentials is not None : with open ( "token.json" , "w" ) as f: f.write(credentials.to_json()) return credentials USER_CREDENTIALS = authorize() def create_event (): """Calendar API ã䜿çšããŠã€ãã³ããäœæãã""" # ãµãŒãã¹ãªããžã§ã¯ãã®æ§ç¯ service = build( "calendar" , "v3" , credentials=USER_CREDENTIALS) # æå®ããæå»ãã1æéã®äºå®ãäœæ now = datetime.datetime.now() startTime = now.isoformat() endTime = (now + datetime.timedelta(hours= 1 )).isoformat() event = { "summary" : "Google Calendar Meet Test Event" , "start" : { "dateTime" : startTime, "timeZone" : "Asia/Tokyo" , }, "end" : { "dateTime" : endTime, "timeZone" : "Asia/Tokyo" , }, "conferenceData" : { "createRequest" : { "requestId" : str (uuid.uuid4()), "conferenceSolutionKey" : { "type" : "hangoutsMeet" }, } }, } # èªèº«ã®ã«ã¬ã³ããŒã«äºå®ã远å event = service.events().insert(calendarId= "primary" , body=event, conferenceDataVersion= 1 ).execute() return event def update_space (meetUri: str = None ): """Meet API ã䜿çšã㊠æ¢åã® Meet äŒè°ãã¢ãŒãã£ãã¡ã¯ãçæãæå¹åãã Space ãªãœãŒã¹ã«æŽæ°ãã""" # google-api-python-client ã§ã®å®è£
service = build( "meet" , "v2" , credentials=USER_CREDENTIALS) # meetUri ãã Space åãæœåº name = f "spaces/{meetUri.split('/')[-1]}" # 倿Žãã Space ãªãœãŒã¹ã®ããã£å®çŸ© space_body = { "config" : { "artifactConfig" : { "recordingConfig" : { "autoRecordingGeneration" : "ON" , }, "transcriptionConfig" : { "autoTranscriptionGeneration" : "ON" , }, "smartNotesConfig" : { "autoSmartNotesGeneration" : "ON" , }, } } } # æŽæ°ãã察象ã®å®çŸ© update_mask = "config.artifactConfig.recordingConfig,config.artifactConfig.transcriptionConfig,config.artifactConfig.smartNotesConfig" # Space ãªãœãŒã¹ã®æŽæ° return service.spaces().patch(name=name, body=space_body, updateMask=update_mask).execute() def main (): """ã¡ã€ã³åŠç""" # Meet äŒè° ãå«ãã ã€ãã³ãã®äœæ event = create_event() print (f "Google Calendar URL {event.get('htmlLink')}" ) meet_uri = event[ "conferenceData" ][ "entryPoints" ][ 0 ][ "uri" ] print (f "Meet URL {meet_uri}" ) # Space ã®èšå®ãæŽæ° update_space(meetUri=meet_uri) if __name__ == "__main__" : main() 以åã®èšäºãšã®çžéç¹ã¯ä»¥äžã§ãã authorize 颿°ã® scopes ã§ã https://www.googleapis.com/auth/meetings.space.settings ãæå®ããŠããŸãããã®ã¹ã³ãŒããæå®ããããšã§ãGoogle Meet é話ãã¹ãŠã®èšå®ã®è¡šç€ºããã³ç·šéãå¯èœã§ãã create_event 颿°ã§ conferenceData ãã£ãŒã«ãã䜿çšããŠãMeet äŒè°ã®èšå®ã远å ããŸãã insert ã¡ãœããåŒã³åºãæã®åŒæ°ãšããŠã conferenceDataVersion=1 ã远å ããMeet äŒè°ãå«ãã€ãã³ããäœæããŸãã 以åã®èšäºãšç°ãªãã®ã¯ã update_space 颿°ã«ããã以äžã®å®è£
ã§ãã ã€ãã³ãã«çŽã¥ã Meet äŒè°ã® URI ãåŒæ°ã«ååŸãã https://meet.google.com/{meetingCode} ã spaces/{meetingCode} ã®åœ¢åŒã«å€æããŸãã Google Meet ã®ææç©äœæãæå¹åããèšå®ã JSON 圢åŒã§å®çŸ©ããŸãã æŽæ°ãããã£ãŒã«ãæ
å ±ãæååã§å®çŸ©ããŸããJSON ã®ãã¹ãã¯ãããïŒ . ïŒåºåããè€æ°ã®ãã£ãŒã«ããæå®ãããå Žåã¯ãã«ã³ãïŒ , ïŒåºåãã§è¡šçŸããŸãã åŠç 1. ã 2. ã 3. ã§å®çŸ©ãã倿°ãåŒæ°ã« patch ã¡ãœãããå®è¡ããããšã§ãMeet äŒè°ã®èšå®ãæŽæ°ããŸãã ãšã©ãŒ åŠç 3. ã®å®è¡æïŒ110è¡ç®ïŒã«ã以äžã®äŸå€ãçºçããŸãã äŸå€ãçºçããŸãã: HttpError <HttpError 403 when requesting https://meet.googleapis.com/v2/spaces/ {meetingCode}?updateMask=config.artifactConfig.recordingConfig%2Cconfig.artifactConfig.transcriptionConfig%2Cconfig.artifactConfig.smartNotesConfig&alt=json returned "Permission denied on resource Space (or it might not exist)". Details: "Permission denied on resource Space (or it might not exist)"> åå ãšã©ãŒã¡ãã»ãŒãžã«ãHTTP ã®ã¬ã¹ãã³ã¹ã¹ããŒã¿ã¹ã³ãŒãã® 403 ãè¿åŽãããŠãããããªã¯ãšã¹ãããæäœãè¡ãæš©éããªããããšãããããŸãã Meet REST API ã®ä»æ§ãšããŠãèªèº«ã§äœæããã¹ããŒã¹ã«å¯ŸããæŽæ°ã®ã¿ã蚱容ããŠãããã«ã¬ã³ããŒãªã©ã®å€éšã®æäœã§äœæãããã¹ããŒã¹ãæŽæ°ã§ããªãããšãåå ãšæšæž¬ãããŸãã åè : REST API ã¹ã³ãŒãã«ã€ã㊠åè : Google Meet API . spaces åé¿ç 以äžã®å®è£
ã«å€æŽããããšã§ãæåŸ
ããåäœã«ãªããŸãã create_event 颿° ã§ã¯ãã€ãã³ãäœææã« Meet äŒè°ãäœæãããªãããã«èšå®ããŸãã google-api-python-client ã©ã€ãã©ãªçµç±ã§ Meet REST API ãåŒã³åºããã¢ãŒãã£ãã¡ã¯ãçæãæå¹åãããã¹ããŒã¹ãäœæããŸãã Calendar API ã䜿çšããŠãäœæããã€ãã³ããšã¹ããŒã¹ãçŽã¥ãããŸãã é«å®® æ (èšäºäžèЧ) ã¯ã©ãŠããœãªã¥ãŒã·ã§ã³éšã¯ã©ãŠããšã¯ã¹ãããŒã©èª² 2025幎6æãããG-genã«ãžã§ã€ã³ãåè·ã¯ååœã®SIerã§é»åãè£œé æ¥ç³»ã®ã客æ§ã«å¯ŸããŠãPMïŒAPãšã³ãžãã¢ãšããŠãèŠä»¶å®çŸ©ããéçšä¿å®ãŸã§å
šå·¥çšãæ
åœãçŸåšã¯Google CloudãåŠã³ãªããããã«ã¹ã¿ãã¯ãšã³ãžãã¢ãç®æããŠã¯ã©ãŠããšã³ãžãã¢ãšããŠã®ã¹ãã«ãç¿åŸäžã Follow @Ggen_RTakamiya